[BOJ] 2618번 경찰차 - C++

"동적 계획법과 최단거리 역추적"

Posted by Yongmin on August 12, 2021

문제

경찰차

풀이

DFS, DP를 이용한 최단거리 확인과 역추적 문제이다. 처음에는 그리디하게 매 경우때마다 최소 거리를 더하면 되는 코드를 작성했지만, 이는 결과적으로 전체 사건을 해결하였을 때 최소 거리라는것을 보장하지 못한다. Reference에서 코드를 가져왔다. DP[i][j]는 경찰차 1이 i번째 사건을, 경찰차 2가 j번째 사건을 해결하였을 때, 2대의 경찰차가 움직여야하는 남은 최소 거리를 의미한다. 그러므로, 원하는 결과는 DP[0][0]의 값이 되겠다. 역추적은 경찰차 1이 next_event로 갔을 때 남은 최소 이동거리와, next_event로 가는 거리의 합을 경찰차 2의 경우와 비교하여 낮은 값의 경찰차 번호를 출력해주면 된다.

소스 코드

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
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <cstring>
 
#define endl "\n"
#define MAX 1010
using namespace std;
 
struct COORD
{
    int x;
    int y;
};
 
int N, W;
int DP[MAX][MAX];
COORD Event[MAX];
 
int Max(int A, int B) { return A > B ? A : B; }
int Min(int A, int B) { return A < B ? A : B; }
 
void Input()
{
    cin >> N >> W;
    for (int i = 1; i <= W; i++)
    {
        cin >> Event[i].x >> Event[i].y;
    }
}
 
int Find_Dist(int x, int y, int xx, int yy) { return abs(xx - x) + abs(yy - y); }
 
int Total_Distance(int Police1, int Police2)
{
    if (Police1 == W || Police2 == W) return 0;
    if (DP[Police1][Police2] != -1) return DP[Police1][Police2];
    
    int Next_Event = Max(Police1, Police2) + 1;
    int Dist1, Dist2;
 
    if (Police1 == 0) Dist1 = Find_Dist(1, 1, Event[Next_Event].x, Event[Next_Event].y);
    else Dist1 = Find_Dist(Event[Police1].x, Event[Police1].y, Event[Next_Event].x, Event[Next_Event].y);
 
    if (Police2 == 0) Dist2 = Find_Dist(N, N, Event[Next_Event].x, Event[Next_Event].y);
    else Dist2 = Find_Dist(Event[Police2].x, Event[Police2].y, Event[Next_Event].x, Event[Next_Event].y);
 
    int Result1 = Dist1 + Total_Distance(Next_Event, Police2);
    int Result2 = Dist2 + Total_Distance(Police1, Next_Event);
    DP[Police1][Police2] = Min(Result1, Result2);
    return DP[Police1][Police2];
}
 
void Route(int P1, int P2)
{
    if (P1 == W || P2 == W) return;
    
    int Next_Event = Max(P1, P2) + 1;
    int Dist1, Dist2;
 
    if (P1 == 0) Dist1 = Find_Dist(1, 1, Event[Next_Event].x, Event[Next_Event].y);
    else Dist1 = Find_Dist(Event[P1].x, Event[P1].y, Event[Next_Event].x, Event[Next_Event].y);
 
    if (P2 == 0) Dist2 = Find_Dist(N, N, Event[Next_Event].x, Event[Next_Event].y);
    else Dist2 = Find_Dist(Event[P2].x, Event[P2].y, Event[Next_Event].x, Event[Next_Event].y);
 
    if (DP[Next_Event][P2] + Dist1 < DP[P1][Next_Event] + Dist2)
    {
        cout << 1 << endl;
        Route(Next_Event, P2);
    }
    else
    {
        cout << 2 << endl;
        Route(P1, Next_Event);
    }
}
 
void Solution()
{
    memset(DP, -1, sizeof(DP));
    cout << Total_Distance(0, 0) << endl;
    Route(0, 0);
}
 
void Solve()
{
    Input();
    Solution();
}
 
int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
 
    //freopen("Input.txt", "r", stdin);
    Solve();
 
    return 0;
}


//출처: https://yabmoons.tistory.com/644 [얍문's Coding World..]

실패한 코드

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
#include <iostream>
#include <vector>

using namespace std;

int N, W;
int map[1001][1001];
vector<pair<int, int>> testcase(1001);
vector<pair<pair<int, int>, pair<int, int>>> dp(1001); // i번째 사건을 해결했을 때, 경찰차 1, 2의 위치
vector<int> car;
int main(){
    scanf("%d", &N);
    
    scanf("%d", &W);
    
    for(int i = 1; i<=W; i++){
        scanf("%d %d", &testcase[i].first, &testcase[i].second); //(y, x);
    }
    
    dp[0].first.first = 1;
    dp[0].first.second = 1; // 경찰차 1 초기 위치 (1,1)
    dp[0].second.first = N;
    dp[0].second.second = N; // 경찰차 2 초기 위치 (N,N)
    //초기값 설정
    
    int cnt = 0;
    for(int i = 1; i<=W; i++){
        int y_1 = abs(testcase[i].first - dp[i-1].first.first);
        int x_1 = abs(testcase[i].second - dp[i-1].first.second);
        // 경찰차 1의 위치와 i번째 사건과의 거리
        int y_2 = abs(testcase[i].first - dp[i-1].second.first);
        int x_2 = abs(testcase[i].second - dp[i-1].second.second);
        // 경찰차 2의 위치와 i번째 사건과의 거리
        
        if(y_1 + x_1 < y_2 + x_2){ // 경찰차1이 더 가깝다면...
            car.push_back(1);
            cnt += y_1 + x_1;
            dp[i].first.first = testcase[i].first;
            dp[i].first.second = testcase[i].second;
            //i번째 사건을 해결했을 때, 경찰차 1의 위치는 i번째 사건의 위치
            dp[i].second.first = dp[i-1].second.first;
            dp[i].second.second = dp[i-1].second.second;
            //경찰차 2의 위치는 그대로
        }
        else{
            car.push_back(2);
            cnt += y_2 + x_2;
            dp[i].first.first = dp[i-1].first.first;
            dp[i].first.second = dp[i-1].first.second;
            //경찰차 1의 위치는 그대로
            dp[i].second.first = testcase[i].first;
            dp[i].second.second = testcase[i].second;
            //i번째 사건을 해결했을 때, 경찰차 2의 위치는 i번째 사건의 위치
        }
        
    }
    
    printf("%d\n", cnt);
    
    for(int i = 0; i<car.size(); i++){
        printf("%d\n", car[i]);
    }
    
    
}


# # #