일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- DP
- 시뮬레이션
- 재귀
- 클래스
- 피보나치 수
- dfs
- 이분 탐색
- Knapsack
- MST
- 비트마스크
- 조합
- BFS
- 링크드리스트
- 문자열
- 크루스칼
- 분할 정복
- 큐
- 완전 탐색
- 그리디
- 중복 순열
- 스택
- 세그먼트 트리
- 메모리풀
- BeautifulSoup
- 우선순위 큐
- lis
- 백트래킹
- 순열
- SSAFY
- 빠른 입출력
- Today
- Total
작심 24/7
[백준] 1525번 퍼즐 (C++, JAVA) 본문
1525번: 퍼즐
세 줄에 걸쳐서 표에 채워져 있는 아홉 개의 수가 주어진다. 한 줄에 세 개의 수가 주어지며, 빈 칸은 0으로 나타낸다.
www.acmicpc.net
관리를 편하게 하기 위해 이차원 배열로 되어 있는 값들을 일차원으로 생각하면
123456780 으로 나타내는 게 목표가 된다.
빈칸의 상하좌우를 검사해주며 빈칸과 숫자를 교환하고 그 값이 목표와 일치하면
같이 저장되어 있던 횟수를 출력, 큐가 empty할 때까지도 목표를 못 만나면 -1을 출력한다.
큐에 값을 집어넣어줄 땐 빈칸과 숫자의 교환을 쉽게 하기 위해서
string 타입으로 숫자를 넣어주는데 이때 0의 위치(빈칸 위치)와
그 숫자까지 가는데 걸린 횟수도 같이 넣어준다.
ex) ("360812745", 2, 이전 수의 횟수 + 1)
이차원이었을 때 빈칸의 상하좌우는 일차원일 때에 맞게 바꾸어서 계산해준다.
검사해줄 때 이미 방문한 수는 다시 방문할 필요가 없으므로 방문 체크를 해주어야 하는데
원래 하던 대로 일차원 배열로 방문 체크를 해주려면
최대 인덱스가 visited[876543210]까지 되어서 32MB인 메모리 제한에 걸리게 된다.
그래서 map의 key와 value를 이용해보았다.
key에는 string으로 수를 넣고 value는 bool 타입으로 방문의 유무만 체크해준다.
범위를 벗어나는 경우를 처리하는 수식 같은 게 생각 안 나서
그냥 if문으로 상, 하, 좌, 우 에 따라서 좀 무식하게 처리했는데
더 이상 생각하기 싫어서 놔뒀다.
(혹시 좋은 아이디어 있으면 댓글 남겨 주세요..!)
< C++ 코드 >
#include <iostream>
#include <queue>
#include <map>
#include <string>
using namespace std;
queue < pair<string, pair<int, int>>> q;
map <string, bool> m;
int dx[4] = { -3, 3, -1, 1 };
int success = -1, idx;
string tmp;
char tmp2;
void Bfs() {
while (!q.empty()) {
if (q.front().first == "123456780") {
success = q.front().second.second;
break;
}
for (int i = 0; i < 4; i++) {
tmp = q.front().first;
idx = q.front().second.first + dx[i]; //0의 위치를 기준으로 상하좌우 검색
if (i == 0 && !(idx >= 0 && idx < 6)) continue; //상
else if (i == 1 && !(idx >= 3 && idx < 9)) continue; //하
else if (i == 2 && !(idx >= 0 && idx < 8 && idx != 2 && idx != 5)) continue; //좌
else if (i == 3 && !(idx >= 1 && idx < 9 && idx != 3 && idx != 6)) continue; //우
tmp2 = tmp[idx];
tmp[idx] = tmp[q.front().second.first];
tmp[q.front().second.first] = tmp2; //위치 교환
if (!m.count(tmp)) {
m[tmp] = true;
q.push(make_pair(tmp, make_pair(idx, q.front().second.second + 1)));
}
}
q.pop();
}
}
int main() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cin >> tmp2;
if (tmp2 == '0') idx = tmp.size();
tmp += tmp2;
}
}
q.push(make_pair(tmp, make_pair(idx, 0)));
Bfs();
cout << success;
return 0;
}
< JAVA 코드 >
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Scanner;
class pair{
int x, cnt;
String s;
pair(int x, int cnt, String s){this.x = x; this.cnt = cnt; this.s = s;}
}
public class BOJ_1525_퍼즐 {
private static String before = "", goal = "123456780";
private static int res = -1;
private static int dx[] = {-3, 3, -1, 1};
private static Map <String, Boolean> chk = new HashMap<>();
private static Queue <pair> q = new LinkedList<>();
public static String swap(String s, int a, int b) { // 교환
StringBuilder sb = new StringBuilder(s);
char tmp = s.charAt(a);
sb.setCharAt(a, s.charAt(b));
sb.setCharAt(b, tmp);
return sb.toString();
}
public static void bfs() {
while(!q.isEmpty()) {
if(q.peek().s.equals(goal)) { // 현재 모습이 목표 모습이면 성공
res = q.peek().cnt;
return;
}
for(int i = 0; i < 4; i++) {
int x = q.peek().x + dx[i], cnt = q.peek().cnt + 1;
// 범위를 벗어나면 이동하지 않는다.
if (i == 0 && !(x >= 0 && x < 6)) continue; //상
else if (i == 1 && !(x >= 3 && x < 9)) continue; //하
else if (i == 2 && !(x >= 0 && x < 8 && x != 2 && x != 5)) continue; //좌
else if (i == 3 && !(x >= 1 && x < 9 && x != 3 && x != 6)) continue; //우
String s = swap(q.peek().s, q.peek().x, x); // 이동한다.
if(chk.get(s) != null) continue; // 이동한 모습이 이전에 이미 나왔었으면 무한 반복 상태이므로 넘긴다.
chk.put(s, true); // 그게 아니라면 방문 표시
q.offer(new pair(x, cnt, s)); // 새로운 모습에서 0의 위치, 증가된 이동 횟수, 새로운 모습 을 큐에 저장한다
}
q.poll();
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) before += sc.nextInt();
q.offer(new pair(before.indexOf("0"), 0, before));
chk.put(before, true);
bfs();
System.out.println(res);
}
}
'백준' 카테고리의 다른 글
[백준] 9465번 스티커 (0) | 2020.06.20 |
---|---|
[백준] 1039번 교환 (0) | 2020.06.19 |
[백준] 9019번 DSLR (0) | 2020.06.19 |
[백준] 16397번 탈출 (C++, JAVA) (0) | 2020.06.18 |
[백준] 2206번 벽 부수고 이동하기 (C++) (0) | 2020.06.16 |