동적계획법을 통해 현재 자물쇠의 상태를 저장하여 푸는 문제이다.
나사가 몇번 회전했는지도 구해야 하므로 dp 테이블 뿐만 아니라 역추적 테이블도 생성해야 한다.
각 테이블의 정의는 다음과 같다.
dp 테이블
dp[i][j] := i번째 나사가 현재 상태에서 j번 왼쪽으로 회전한 상태일 때 원하는 숫자로 맞추기 위해 회전한 칸수의 최소
역추적 테이블
backward[i][j] := i번째 나사가 왼쪽으로 j번 회전한 상태일 때 최적해에 도달하기 위해 왼쪽으로 회전한 칸수
바텀 업 dp로 현재 나사에서 원하는 상태를 오른쪽으로 돌려서 맞췄을 때, 왼쪽으로 돌려서 맞췄을 때를 계산해준다. 회전 칸수는 모듈러 연산을 이용해 구해준다.
이 과정에서 최적해가 갱신된다면 회전한 칸수 j를 backward에 저장해준다.
이렇게 동적계획법을 수행하고나면 최적해는 n번째 나사에서 0 ~ 9칸 왼쪽으로 회전한 상태 중 최소값이 된다.
어떻게 회전했는지 알기 위해 최적해가 될 때 j를 가지고 다시 뒤에서부터 역추적을 해보자.
우리는 각 나사가 최적해에 도달하기 위해 왼쪽으로 회전한 칸수를 backward에 저장했다.
backward[i][j] = j면 i번째 나사가 최적해로 도달하기 위해 오른쪽으로 나사를 돌렸다는 뜻이고 그렇지 않으면 왼쪽으로 나사를 돌렸다는 뜻이 되기 때문에 방향성을 판별할 수 있다.
오른쪽으로 나사를 돌린 경우 출력값은 -(dp[i][j] - dp[i - 1][j])가 된다.
왼쪽으로 나사를 돌린 경우 출력값은 dp[i][j] - dp[i - 1][이전 나사에서 왼쪽으로 회전한 칸수]가 된다.
이 출력값을 배열에 저장해주고 형식에 맞게 출력하면 된다.
전체 코드
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
|
#include <bits/stdc++.h>
using namespace std;
int n;
int start[10001], goal[10001];
int dp[10001][10];
int backward[10001][10];
int reconstruct[10001];
inline int circular(int x)
{
while (x < 0)
x += 10;
return x % 10;
}
int main()
{
fill(&dp[0][0], &dp[0][0] + 10001 * 10, 1e9);
cin.tie(0); ios::sync_with_stdio(false);
cin >> n;
cin.get();
for (int i = 1; i <= n; ++i)
start[i] = cin.get() - '0';
cin.get();
for (int j = 1; j <= n; ++j)
goal[j] = cin.get() - '0';
for (int i = 0; i < 10; ++i)
dp[0][i] = i;
for (int i = 1; i <= n; ++i)
{
for (int j = 0; j < 10; ++j)
{
int left = circular(goal[i] - start[i] - j);
int right = circular(-left);
if (dp[i][j] > dp[i - 1][j] + right)
{
dp[i][j] = dp[i - 1][j] + right;
backward[i][j] = j;
}
if (dp[i][circular(j + left)] > dp[i - 1][j] + left)
{
dp[i][circular(j + left)] = dp[i - 1][j] + left;
backward[i][circular(j + left)] = j;
}
}
}
int res = 1e9, minshft;
for (int i = 0; i < 10; ++i)
if (res > dp[n][i])
res = dp[n][i], minshft = i;
cout << res << '\n';
int idx = n;
while (idx)
{
if (backward[idx][minshft] == minshft)
reconstruct[idx] = -(dp[idx][minshft] - dp[idx - 1][minshft]);
else
reconstruct[idx] = dp[idx][minshft] - dp[idx - 1][backward[idx][minshft]];
minshft = backward[idx--][minshft];
}
for (int i = 1; i <= n; ++i)
if (reconstruct[i])
cout << i << " " << reconstruct[i] << '\n';
}
|
cs |
'백준 > DP' 카테고리의 다른 글
[KOI] 백준 1866 - 택배 (0) | 2021.01.28 |
---|---|
백준 15678 - 연세워터파크 (0) | 2021.01.26 |
백준 1514 - 자물쇠 (0) | 2021.01.18 |
[COCI] 백준 10740 - ACM (0) | 2021.01.13 |
[ICPC] 백준 2135 - 문자열 압축하기 (0) | 2021.01.10 |