ps

[백준] 10251 - 운전 면허 시험

kariskan 2023. 7. 5. 12:04

https://www.acmicpc.net/problem/10251

 

10251번: 운전 면허 시험

만일 G 이하의 연료량으로 s에서 t까지 가는 것이 가능하다면 가능한 한 빨리 도착했을 때 걸리는 시간을, 불가능하다면 -1을 출력한다.

www.acmicpc.net

 

격자 내에서 최단 거리로 이동할 때, 일정 가중치 이하로 이동하면서 방향을 가장 적게 구하는 경로를 구하는 문제이다.

격자 내에서 최단 거리로 이동하는 문제는 보통 dp로 해결할 수 있으므로, 이번에도 dp로 해결해 보려고 한다.

 

문제의 제약조건을 더 살펴보자.

G 이하의 연료만을 사용해야 하고, 방향을 바꾸는 데 걸리는 시간이 1로 존재한다.

따라서 dp 배열을 다음과 같이 정의하였다.

 

dp[i][j][k][l] = l 방향(0일 때 오른쪽, 1일 때 밑)으로 도착했고, 방향을 k번 바꾸면서 {i,j}에 도착했을 때 드는 연료의 최솟값

 

이렇게 dp 배열만 선언하면 이후엔 경우를 나누어서 간단하게 문제를 해결할 수 있다.

다만 주의해야할 점은 최대 방향을 바꾸는 횟수가 약 200번이라는 점에서, k의 크기를 최소 200으로 해야 한다.

 

#include <iostream>
using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int m, n, l, g;
        cin >> n >> m >> l >> g;
        // {오른쪽으로 갈때 드는 연료, 밑으로 갈때 드는 연료}
        pair<int, int> board[101][101];

        // 마지막으로 l방향(0일때 오른쪽, 1일때 밑)으로 도착했고,
        // 방향을 k번 바꾸면서 {i,j}에 도달할 때
        // 드는 연료
        int dp[101][101][301][2] = {0};

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m - 1; j++) {
                cin >> board[i][j].first;
            }
        }
        for (int i = 1; i <= n - 1; i++) {
            for (int j = 1; j <= m; j++) {
                cin >> board[i][j].second;
            }
        }
        
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (i == 1 && j == 1) {
                    continue;
                } else if (i == 1) {
                    dp[i][j][0][0] = dp[i][j - 1][0][0] + board[i][j - 1].first;
                } else if (j == 1) {
                    dp[i][j][0][1] = dp[i - 1][j][0][1] + board[i - 1][j].second;
                } else {
                    for (int k = 0; k <= 300; k++) {
                        if (dp[i - 1][j][k][1]) {
                            if (dp[i][j][k][1] == 0 || dp[i][j][k][1] > dp[i - 1][j][k][1] + board[i - 1][j].second) {
                                dp[i][j][k][1] = dp[i - 1][j][k][1] + board[i - 1][j].second;
                            }
                        }
                        if (dp[i - 1][j][k][0]) {
                            if (dp[i][j][k + 1][1] == 0 || dp[i][j][k + 1][1] > dp[i - 1][j][k][0] + board[i - 1][j].second) {
                                dp[i][j][k + 1][1] = dp[i - 1][j][k][0] + board[i - 1][j].second;
                            }
                        }
                        
                        if (dp[i][j - 1][k][1]) {
                            if (dp[i][j][k + 1][0] == 0 || dp[i][j][k + 1][0] > dp[i][j - 1][k][1] + board[i][j - 1].first) {
                                dp[i][j][k + 1][0] = dp[i][j - 1][k][1] + board[i][j - 1].first;
                            }
                        }
                        if (dp[i][j - 1][k][0]) {
                            if (dp[i][j][k][0] == 0 || dp[i][j][k][0] > dp[i][j - 1][k][0] + board[i][j - 1].first) {
                                dp[i][j][k][0] = dp[i][j - 1][k][0] + board[i][j - 1].first;
                            }
                        }
                    }
                }
            }
        }
        
        int ans = -1;
        for (int i = 0; i <= 300; i++){
            int mi;
            if (dp[n][m][i][0] == 0 && dp[n][m][i][1] == 0) {
                continue;
            } else if (dp[n][m][i][0] == 0){
                mi = dp[n][m][i][1];
            } else if (dp[n][m][i][1] == 0) {
                mi = dp[n][m][i][0];
            } else {
                mi = min(dp[n][m][i][0], dp[n][m][i][1]);
            }
            if (mi <= g) {
                ans = i;
                break;
            }
        }
        
        if (ans == -1) {
            cout << ans << '\n';
        } else {
            cout << l * (n + m - 2) + ans << '\n';
        }
    }
}