본문 바로가기

BOJ 문제풀이

#1987 알파벳

 

https://www.acmicpc.net/problem/1987

 

1987번: 알파벳

세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다. 말은 상하좌우로 인접한 네 칸 중의 한 칸으

www.acmicpc.net

골드 4의 문제이다. 처음 봤을때 이런문제가 왜 골드 4이지? 생각을 했었다. 코드도 쭉쭉 잘 써내려가서 테스트케이스까지 잘 통과하였다. 근데 제출하니까 2%에서 틀렸다고 나왔다. 보자마자 문제가 있음을 인지하고 직접 손으로 써보면서 dfs를 따라가보니까 문제에서 원하는 방향과는 다르게 답이 나오고있었다.

내가 쓰지않은 알고리즘이 있었던것이다. 바로 백트래킹 이다.

 

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

int r,c,ans;
char board[21][21];
int check[26];
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};

void dfs(int x,int y,int cnt){
  if(cnt>ans)
    ans=cnt;
  check[board[x][y]-'A']=1;
  for(int k=0;k<4;k++){
    int nx=x+dx[k];
    int ny=y+dy[k];
    if(0<=nx&&nx<r && 0<=ny && ny<c){
      if(check[board[nx][ny]-'A']==0){
        check[board[nx][ny]-'A']=1;
        dfs(nx,ny,cnt+1);
        check[board[nx][ny]-'A']=0;
      }
    }
  }
}

int main()
{
  freopen("input.txt","r",stdin);
  cin>>r>>c;
  for(int i=0;i<r;i++){
    for(int j=0;j<c;j++){
      cin>>board[i][j];
    }
  }
  dfs(0,0,1);
  
  cout<<ans;
}

처음에는 백트래킹 따위는 고려하지않고 어떤 정점에서 4방향을 따져본다음 나오지않았던 알파벳이라면 탐색하는 방식으로 진행했었다. 그렇게 하면 이상한 방법으로 탐색을 진행 할 수도 있다. 예를들어 문제의 2번째 테케에서, 답은 

H->F->J->A->D->G 로 6인데, 내 코드에서는 H->F->D->(되돌아가서) J->G->A 로 6이 나왔다.

이 부분에서 백트래킹이 필요했던 것이다. 특정 정점으로 들어갔을때 답이 나오지않을것 같다 싶으면 다시 돌아와서 다른 방향으로 탐색을 진행해야한다. 

 

그래서 dfs를 구현할때 x,y좌표 뿐만아니라 cnt를 선언하여 답을 구할 수 있게 하였다.

핵심 코드는 다음과 같다.

for(int k=0;k<4;k++){
    int nx=x+dx[k];
    int ny=y+dy[k];
    if(0<=nx&&nx<r && 0<=ny && ny<c){
      if(check[board[nx][ny]-'A']==0){
        check[board[nx][ny]-'A']=1;
        dfs(nx,ny,cnt+1);
        check[board[nx][ny]-'A']=0;
      }
    }
  }

여기서 check배열에 특정 알파벳이 등장했다면 1을 넣어주고, check배열이 0이라면 등장한적 없던 알파벳인 것이다.

if(check[board[nx][ny]-'A']==0){
    check[board[nx][ny]-'A']=1;
    dfs(nx,ny,cnt+1);
    check[board[nx][ny]-'A']=0;

}

이 부분에서  check[board[nx][ny]-'A']=0; 이 코드가 핵심이다. 왜냐면 나온적 없는 알파벳이라면 check에서 0 일테니까 if문을 충족하여  check[board[nx][ny]-'A']=1; 이 부분을 실행하여 나왔다고 기록한다. 그리고 dfs를 재귀적으로 호출해 그 주변의 정점을 탐색하고 돌아온다. 그리고 다시 0을 대입해 쓴적 없다고 기록하는것이다. 왜냐면 앞으로 다시 나왔을때 방문해야할 수도 있기 때문!

 

문제가 쉬워 보여서 골드4여도 쉬운가보다 했는데 역시 아니었다 ㅎㅎ;;; 백트래킹 문제를 더 연습해야겠다.

 

'BOJ 문제풀이' 카테고리의 다른 글

#1003 피보나치 함수  (0) 2022.04.11
#1926 그림  (0) 2022.04.10
#2573 빙산  (0) 2022.04.09
#14503 로봇 청소기  (0) 2022.04.08
#10451 순열 사이클  (0) 2022.04.06