본문 바로가기
백준

백준 1525 퍼즐

by 콩순이냉장고 2021. 1. 1.

문제 URL : www.acmicpc.net/problem/1525

 

1525번: 퍼즐

세 줄에 걸쳐서 표에 채워져 있는 아홉 개의 수가 주어진다. 한 줄에 세 개의 수가 주어지며, 빈 칸은 0으로 나타낸다.

www.acmicpc.net

문제 접근법

 

3*3 배열   

1 2 3
4 5 6
7 8 0

인 배열형태로 만들면 끝나는 문제입니다. 그러나 어떻게 만들지 고민하셔야합니다.

첫번째 : 처음 숫자는 무작위로 주어지고 0인 숫자에서 좌우상하 자리를 바꾸어가면서 BFS를 이용하여 구하셔야합니다.

두번째 : 3*3 배열의 visit을 어떻게 처리할까? 이게 핵심이었습니다. 여러가지를 생각했지만 string의 해쉬코드를 응용하는 것이었습니다.

일반적인 string의 해시코드는

해쉬코드

이러한 공식을 성립합니다. 공식에 나와있는  p는 소수이고 m은 적당히 큰수, s[i]는 ascii코드 

즉 3*3 을 일차원인 배열로 만들때

1 2 3 4 5 6 7 8 0

이런형태가 끝이 되겠죠 이것을 문자열로 보면

"123456780" 이고 이것과 동일한 해쉬코드를(코드가 몇인지는 몰라도됨) 가질수있는 문자는 123456780뿐입니다.

그이유는 9개를 입력받고 0~8까지 중복없이 정확히 9개가 입력된다는것이 보장이 됩니다.

이러한 해쉬코드를 찾아서 visit을 처리한다면 3*3배열을 통해서 visit을해결할 수 있습니다.

 

세번째: 문자열의 해쉬코드를 이용할때 소수인 p는 대부분 31을 이용합니다. 

처음 문제를 풀었을땐 31을 이용했지만

이문제는 입력조건이 제한적이라 소수가아닌 10을 이용해도 해쉬코드는 중복될 수 없습니다.

 

소스코드:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//By 콩순이냉장고
#include <iostream>
#include <unordered_set>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
int dy[4= { -1010 };
int dx[4= { 010-1 };
ll map[3][3];
struct puzzle{
    ll board[3][3];
    int cnt;
    int y;//0위치 좌표
    int x;
    puzzle(ll board[3][3],int cnt){
        this->cnt = cnt;
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++){
                this->board[i][j] = board[i][j];
                if (board[i][j] == 0){
                    y = i;
                    x = j;
                }
            }
        }
    }
};
ll hashcode(ll board[3][3]){
    ll idx=0;
    ll h = 0;
    ll hcode = 10;//원래 문자열은 31인 소수를 이용 하지만 이 문제에선 10이어도 무방
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++){
            h += board[i][j]*pow(hcode,idx++);
        }
    }
    return h;
}
int bfs(){
    
    ll finish[3][3= {
        {1,2,3},
        {4,5,6},
        {7,8,0}
    };
    ll finishcode = hashcode(finish);
    queue<puzzle> q;
    unordered_set<ll> visit;
    q.push({map,0});
    visit.insert(hashcode(map));
    while (!q.empty()){
        puzzle cur = q.front();
        q.pop();
        if (finishcode == hashcode(cur.board))
            return cur.cnt;
        for (int i = 0; i < 4; i++)
        {
            int ny = cur.y + dy[i];//0의 위치에서 
            int nx = cur.x + dx[i];
            if (0 <= ny&&ny < 3 && 0 <= nx&&nx < 3){//동서남북 swap 돌릴수있는지 확인
                ll nboard[3][3];
                memcpy(nboard, cur.board, sizeof(nboard));
                swap(nboard[ny][nx], nboard[cur.y][cur.x]);//swap해주고
                ll nextcode = hashcode(nboard);//다음 움직일 장소의 해쉬코드를 찾음
                if (visit.count(nextcode)==0){//방문한적있는지
                    visit.insert(nextcode);
                    q.push({ nboard, cur.cnt + 1 });
                }
            }
        }
    }
    return -1;
}
int main(){
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++){
            cin >> map[i][j];
        }
    }
    
    cout << bfs() << "\n";
}
cs

 

궁금한점 혹은 모르는점 혹은 어떤질문이든 괜찮습니다.

댓글은 언제나 환영입니다.

 

'백준' 카테고리의 다른 글

백준 20057 마법사 상어와 토네이도  (0) 2021.02.10
백준 3190 뱀  (0) 2021.02.10
벡즌 1929 소수 구하기  (0) 2021.01.01
백준 16938 캠프 준비  (0) 2020.12.31
백준 9944 NxM 보드 완주하기  (0) 2020.12.31