[Baekjoon,14500]테트로미노

1 분 소요

해결

초기에는 모든 도형을 상수 배열에 저장시켜서 반복문을 돌려야 하는 줄 알았다. 그러나 방법은 있었다.

한 붓 그리기

Vue

위 그림에서 한 붓으로 그릴 수 있는 도형은 ‘ㅜ’ 모양을 제외한 도형이다. 즉, 백트래킹을 통해 여러 방향으로 탐색을 하는 중 길이가 4가 되었을 경우 최고 값을 갱신 시키면 된다. 이때 이전에 움직인 방향과 반대가 되는 방향으로 이동하면 방문한 곳을 다시 방문하니 이 부분은 제외해야 한다.

남은 도형인 ‘ㅜ’ 모양은 따로 상수 배열을 만들어 따로 합을 구해 최고 값 갱신 여부를 확인하고 갱신하면 된다.

최적화 하기

더 나아가 최적화를 할 수 있는데, 방법은 가지치기이다.
백트래킹 도중 도형을 완성시켜 봤자 이미 구한 최대 값보다 커질 수 없는 경우 곧 바로 탐색을 종료하는 것이다. 이는 맵에서 가장 큰 원소를 찾아서 계산하면 구현할 수 있다.

구현

실제 구현시 입력 값이 많아 입력 속도가 느린 cin 대신 scanf를 사용했다.

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

const int MAX=500;
const int dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};

// 'ㅗ' 모양의 방향 별 위치
const int ex[4][4]={ {0,-1,0,1},{0,0,1,0},{0,-1,1,0},{0,-1,0,0} };
const int ey[4][4]={ {0,0,-1,0},{0,-1,0,1},{0,0,0,1},{0,0,-1,1} };

int map[MAX][MAX];
int H,W;
int ret,maxNum;

inline bool inRange(const int y, const int x)
{
    return 0<=y && y<H && 0<=x && x<W;
}

// len 길이이고 dir방향으로 움직였고 합이 sum이며 현재 위치가 (y,x)
void dfs(int y, int x, int dir, int sum, int len)
{
    if(len==4)
    {
        ret=max(ret,sum);
        return;
    }
    // 가지치기: 아무리 더해도 현재 얻은 최고 값을 넘길 수 없음
    if(ret>(4-len)*maxNum+sum)
        return;
    for(int i=0;i<4;++i)
        // 이전 방향과 반대 방향은 제외 한다.
        if(abs(dir-i)!=2)
        {
            int ny=y+dy[i];
            int nx=x+dx[i];
            // 종이를 벗어나면 안된다.
            if(!inRange(ny,nx))
                continue;
            dfs(ny,nx,i,sum+map[ny][nx],len+1);
        }
}

// 'ㅗ' 모양을 만들어 합을 구한다.
void calcException(int y, int x)
{
    // 가지치기
    if(ret>3*maxNum+map[y][x])
        return;
    for(int i=0;i<4;++i)
    {
        bool canMake=true;
        int sum=0;
        for(int pos=0;pos<4;++pos)
        {
            int ny=y+ey[i][pos];
            int nx=x+ex[i][pos];
            if(!inRange(ny,nx))
            {
                canMake=false;
                break;
            }
            sum+=map[ny][nx];
        }
        // 조각을 만들 수 있을 경우 
        if(canMake)
            ret=max(ret,sum);
    }
}

int main()
{
    cin>>H>>W;
    maxNum=0;
    for(int y=0;y<H;++y)
        for(int x=0;x<W;++x)
        {
            scanf("%d",&map[y][x]);
            maxNum=max(maxNum,map[y][x]);
        }
    
    // 모든 위치에서 시작해보며 도형을 배치한다.
    for(int y=0;y<H;++y)
        for(int x=0;x<W;++x)
        {
            dfs(y,x,-1,map[y][x],1);
            // 'ㅗ' 조각은 따로 처리한다.
            calcException(y,x);
        }
    
    cout<<ret<<endl;
    return 0;
}

댓글남기기