일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- boj
- hmg소프티어부트캠프
- 실전sql퀵스타트
- 소프티어
- 데이터엔지니어
- Python
- 도커
- ucpc
- 윈도우클린설치
- open(0)
- 소프티어부트캠프
- 현대자동차그룹
- 데이터엔지니어링
- spaghetti code
- 그리디알고리즘
- 피보나치수
- code golf
- 표준입력
- docker
- 파이썬
- 비트집합
- 에라토스테네스의채
- 백준
- 우분투22.04
- IDA*
- Short Coding
- zshrc
- 대학생알고리즘특강
- 알고리즘특강
- 현대자동차
- Today
- Total
Neo Ground
[백준 | Python] #2549 - 루빅의 사각형 (IDA* 알고리즘) 본문
2549번: 루빅의 사각형
첫 번째 줄에는 움직이는 횟수를, 두 번째 줄부터는 한 줄에 하나씩 타일을 움직이는 방법을 순서대로 출력한다. 이때, 격자판의 i번째 행을 k칸 움직였다면 정수 1과 i와 k를 빈칸을 사이에 두고
www.acmicpc.net
문제
4*4의 격자판의 각 타일에 1부터 16까지의 숫자가 적혀있다. 행을 밀거나 열을 밀어서 격자판의 숫자를 순서대로 정렬할 때 그 최소 이동 수와 이동 경로를 출력하라.
아이디어
15퍼즐이 생각나는 문제이다. 15퍼즐은 IDA* 알고리즘을 이용해 그나마 빠른 속도로 해결할 수 있다.
또한 15퍼즐은 각 이동의 소요 비용이 1이므로 IDA* 알고리즘에서 우선순위 큐를 사용할 필요가 없다.
이 문제도 IDA* 알고리즘을 적용할 수 있을 것 같아 보인다.
관건은 휴리스틱 함수를 어떻게 정의하느냐인데 그 아이디어는 다음과 같다.
- 하나의 움직임에 4개의 타일이 움직인다.
- 따라서 위치가 틀린 것이 n개 있다면 그 격자판은 적어도 n/4의 움직임이 필요하다.
결론
- 모든 타일에 대해 행과 열이 적절한 위치인지 검사한다. 적절하지 않다면 distance에 1을 더한다.
- distance를 4로 나눈 몫이 그 격자판의 최적 이동 수의 휴리스틱 함수로 기능한다.
구현
전형적인 IDA* 알고리즘이다.
while loop에서 max_move
에 대한 iterative deepening을 담당하고
heuristic function은 calc_distance(board) // 4
이다.
격자판이 정렬되었는지는 distance == 0
임을 통해 판단할 수 있다.
각 행과 각 열에 대해 1, 2, 3칸 미는 움직임을 모두 재귀로 구현한다.
코드
# 휴리스틱 함수
def calc_distance(board):
dis = 0
for i in range(4):
for j in range(4):
dis += (board[i][j] // 4 != i) + (board[i][j] % 4 != j)
return dis
# A* 재귀
def solve(board, left_move_count, route):
distance = calc_distance(board)
if distance == 0: return route
if left_move_count < distance // 4: return None
for row in range(4):
for moves in range(1,4):
board[row][0],board[row][1],board[row][2],board[row][3] = board[row][3],board[row][0],board[row][1],board[row][2]
sol = solve(board, left_move_count-1, route+[(1, row+1, moves)])
if sol: return sol
board[row][0],board[row][1],board[row][2],board[row][3] = board[row][3],board[row][0],board[row][1],board[row][2]
for col in range(4):
for moves in range(1,4):
board[0][col],board[1][col],board[2][col],board[3][col] = board[3][col],board[0][col],board[1][col],board[2][col]
sol = solve(board, left_move_count-1, route+[(2, col+1, moves)])
if sol: return sol
board[0][col],board[1][col],board[2][col],board[3][col] = board[3][col],board[0][col],board[1][col],board[2][col]
return None
board = [list(map(lambda x: int(x)-1, input().split())) for _ in range(4)]
# iterative deepening
max_move = 0
while 1:
sol = solve(board, max_move, [])
if sol:
print(max_move)
for route in sol:
print(*route)
break
max_move += 1
후기
15퍼즐 처돌이라 플레2 치고 되게 금방 해결했다.
양방향 BFS로 해결하는 방법도 있는 것 같다.
이 부분은 잘 몰라서 공부해야할 것 같다.
C++은 heuristic 없이 iterative deepening DFS로 해결이 가능한 것 같다.
'Problem Solving' 카테고리의 다른 글
[백준] #18789 - 814 - 2 (1) | 2023.12.18 |
---|---|
[백준 | Python] #11444 - 피보나치 수 (cache decorator) (1) | 2023.11.18 |
백준 파이썬 숏코딩 팁 (Python Code Golf) (0) | 2023.08.26 |
[백준 | Python] #11967 - 불켜기 (0) | 2023.07.26 |
[백준 | Python] #9363 - 큰 나눗셈 (0) | 2023.07.25 |