Post

[백준 C++] 2342번 Dance Dance Revolution

백준 2342번 Dance Dance Revolution cpp c++로 풀이

[백준 C++] 2342번 Dance Dance Revolution

2342번: Dance Dance Revolution

문제 요약

문제

승환이는 요즘 “Dance Dance Revolution”이라는 게임에 빠져 살고 있다. 하지만 그의 춤 솜씨를 보면 알 수 있듯이, 그는 DDR을 잘 하지 못한다. 그럼에도 불구하고 그는 살을 뺄 수 있다는 일념으로 DDR을 즐긴다.

DDR은 아래의 그림과 같은 모양의 발판이 있고, 주어진 스텝에 맞춰 나가는 게임이다. 발판은 하나의 중점을 기준으로 위, 아래, 왼쪽, 오른쪽으로 연결되어 있다. 편의상 중점을 0, 위를 1, 왼쪽을 2, 아래를 3, 오른쪽을 4라고 정하자.

처음에 게이머는 두 발을 중앙에 모으고 있다.(그림에서 0의 위치) 그리고 게임이 시작하면, 지시에 따라 왼쪽 또는 오른쪽 발을 움직인다. 하지만 그의 두 발이 동시에 움직이지는 않는다.

이 게임에는 이상한 규칙이 더 있다. 두 발이 같은 지점에 있는 것이 허락되지 않는 것이다. (물론 게임 시작시에는 예외이다) 만약, 한 발이 1의 위치에 있고, 다른 한 발이 3의 위치에 있을 때, 3을 연속으로 눌러야 한다면, 3의 위치에 있는 발로 반복해야 눌러야 한다는 것이다.

오랫동안 DDR을 해 온 백승환은 발이 움직이는 위치에 따라서 드는 힘이 다르다는 것을 알게 되었다. 만약, 중앙에 있던 발이 다른 지점으로 움직일 때, 2의 힘을 사용하게 된다. 그리고 다른 지점에서 인접한 지점으로 움직일 때는 3의 힘을 사용하게 된다. (예를 들면 왼쪽에서 위나 아래로 이동할 때의 이야기이다.) 그리고 반대편으로 움직일때는 4의 힘을 사용하게 된다. (위쪽에서 아래쪽으로, 또는 오른쪽에서 왼쪽으로). 만약 같은 지점을 한번 더 누른다면, 그때는 1의 힘을 사용하게 된다.

만약 1 → 2 → 2 → 4를 눌러야 한다고 가정해 보자. 당신의 두 발은 처음에 (point 0, point 0)에 위치하여 있을 것이다. 그리고 (0, 0) → (0, 1) → (2, 1) → (2, 1) → (2, 4)로 이동하면, 당신은 8의 힘을 사용하게 된다. 다른 방법으로 발을 움직이려고 해도, 당신은 8의 힘보다 더 적게 힘을 사용해서 1 → 2 → 2 → 4를 누를 수는 없을 것이다.

사용 알고리즘

다이나믹 프로그래밍

입출력

  • 입력: 입력은 지시 사항으로 이루어진다. 각각의 지시 사항은 하나의 수열로 이루어진다. 각각의 수열은 1, 2, 3, 4의 숫자들로 이루어지고, 이 숫자들은 각각의 방향을 나타낸다. 그리고 0은 수열의 마지막을 의미한다. 즉, 입력 파일의 마지막에는 0이 입력된다. 입력되는 수열의 길이는 100,000을 넘지 않는다.
  • 출력: 한 줄에 모든 지시 사항을 만족하는 데 사용되는 최소의 힘을 출력한다. ``` 예제1

입력 1 2 2 4 0 출력 8

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
88
89
90
91
## 풀이
dp로 푸는 문제이다. `dp[step][left][right]`배열을 만들어서 스탭 한번당 가능한 경우의 수를 모두 저장한다. dp인 이유는 언뜻 보면 모든 경우의 수를 저장하는것 같이 보이지만 가능한 모든 발의 조합의 경우의 수만 저장하고 있고, 겹치는 발의 조합이 나오면 최소값을 가져온다. dp를 사용하지 않고 모든 조합을 하면 `O(2^n)`이 되었을 것이다.

## 어려웠던 점
처음 접근할때 dp의 중간지점을 잡을때 과거 왼발을 움직였는지 오른발을 움직였는지 2개로 잡았는데 완전 틀린 접근이었다. 중간에 매우 많이 틀렸었던 문제인데 발의 움직임 값을 계산하는 함수에서 인접한 발판으로 이동하는 if문에서 둘의 차가 1일때만 넣어놨는데 3일때도 넣었어야 했다.

## 배운 점 / 느낀 점
dp를 저렇게 많이 잡아서 사용할 수 있다는것도 생각하게 되었다.  내가 푼 모든 테스트 케이스가 1일때만 넣어도 풀어져서 찾는데 고생했다. 당연히 비교함수는 맞을줄 알고 dp부분만 계속 고치고 있었는데 앞으로는 확실히 체크해야겠다. 

## 전체 코드
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
#define INF 1000000000
#define MAX 100009
using namespace std;

vector<int> a;
int dp[MAX][5][5];

int comp(int b, int c){
    if(b == 0){
        return 2;
    }
    else if(abs(b-c) == 1 ||abs(b-c) == 3){
        return 3;
    }
    else if(b==c){
        return 1;
    }
    else{
        return 4;
    }
}


int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    int t;
    a = vector<int>();

    while(true){
        cin >> t;
        if(t == 0){
            break;
        }
        else{
            a.push_back(t);
        }
    }

    for (int i = 0; i < a.size()+3; i++) {
        for (int j = 0; j < 5; j++) {
            for (int k = 0; k < 5; k++) {
                dp[i][j][k] = INF;
            }
        }
    }
    dp[0][0][0] = 0;

    for (int i = 0; i < a.size(); i++) {
        for (int j = 0; j < 5; j++) {
            for (int k = 0; k < 5; k++) {
                if(dp[i][j][k] == INF){
                    continue;
                }
                int next = a[i];
                if (next != k) {
                    dp[i+1][next][k] = min(dp[i+1][next][k], dp[i][j][k] + comp(j, next));
                }
                if (next != j) {
                    dp[i+1][j][next] = min(dp[i+1][j][next], dp[i][j][k] + comp(k, next));
                }
                
            }
        }
    }

    int m = INF;
    for (int j = 0; j < 5; j++) {
        for (int k = 0; k < 5; k++) {
            m = min(m,dp[a.size()][j][k]);
        }
    }
    cout << m;
    
    return 0;
}
This post is licensed under CC BY 4.0 by the author.