코딩/백준 문제 풀이

BOJ 1006 - 습격자 초라기

stonejjun 2020. 2. 9. 14:05

문제 태그 : https://www.acmicpc.net/problem/1006

앞에서 순서대로 푸는 사람들이 절망할만한 문제이다. 1000~1005 보단 확실히 난이도가 있다.

문제 설명

원형 2열로 배치된 2n 개의 칸을 최소 그룹의 갯수로 묶어야 한다. 그룹에는 조건이 있다. 최대 2개가 같은 그룹일 수 있으며, 배치 상에서 연속된 칸이어야 하고, 두 칸의 값의 합이 m을 넘지 않아야 한다.
2칸이 한 줄이고 n줄이 있다고 생각하면 된다. 

문제 풀이

DP를 쓰고 싶은 느낌이 들었다. 어떤 줄까지 다 채우는데 사용된 최소 그룹의 갯수를 이용해 다음 줄까지 채우는데 사용된 최소 그룹의 갯수에 영향을 준다고 생각할 수 있기 때문이다. 하지만 문제가 있다. 어떤 칸을 채울 때에는 이전 칸을 완벽히 채운 상황만 고려해서는 최적의 결과를 얻어낼 수 없다. 왜냐하면 이전칸과 같이 한 그룹을 만들어야 할 상황도 있기 때문이다. 일단 dp[i]은 i번째 줄까지 채우는데 사용된 최소 그룹의 갯수라고 정의하자.

위에서 언급한 문제점을 해결 하기 위해서 모든 경우의 수를 다 따져주면 된다. 어떤 줄의 칸을 모두 채우는 경우만 따지지 말고, 그 줄의 윗칸 까지만 채웠을 때, 아랫칸 까지만 채웠을 때, 두 칸 모두 채웠을 때의 경우의 수를 모두 저장하자. 
어떤 줄의 아랫칸만 채울 때 까지의 값(최소 그룹수) 는 전 줄의 윗칸까지만 채웠을 때 +2 or +1 이다. 이런 식으로 각 상황은 전 상황의 어떤 상황에서 영향을 받을 지 생각하여 식을 세우면 된다.

이 문제에서 요구하는 바는 하나가 더 있다. 바로 직사각형이 아니라 원형이라는 것이다. 따라서 이를 직사각형에서 생각할 때는 첫 줄과 마지막 줄 사이의 한 그룹도 고려를 해주어야 한다. 이에 따른 경우의 수는 총 4가지가 될 것이다.
i) 둘을 연결한 그룹이 없을 경우 ii) 윗칸만 연결 된 경우 iii) 아랫칸만 연결 된 경우 iiii) 둘 다 연결 된 경우
이렇게 4가지에 대해서 모두 돌려줘도 상수이므로 한 케이스당 O(N) 총 O(TN)으로 해결이 가능하다. 

여담

DP에서 강조하는 어떤 값을 구하기 위한 그 세부 문제에 대한 정답을 확실히 알고 그 관계도 확실해야한다. 이 말을 지키기 위해서 할 수 있는 여러가지 방법 중 경우의 수로 쪼개는 방식으로 푸는 문제였다. 위의 풀이를 보면 알 수 있듯이 계속 반복되는 과정이라 코드도 반복이 엄청 많고 길다.

구현

예외 처리를 조심해야 한다. 나는 4가지 시작-끝을 고려하는 것을 맨 첫 줄에서 칸을 때서 마지막 칸 뒤에 붙이는 것으로 처리를 해주었다. 이때 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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
 
int main(){
    ll t;
    scanf("%lld",&t);
    while(t--){
        ll i,j,k,l,m,n;
        ll dp[4][4][10101];
        ll arr[2][10101];
        for(i=0;i<4;i++){
            for(j=0;j<4;j++){
                for(k=1;k<=10100;k++){
                    dp[i][j][k]=1e18;
                }
            }
        }
        scanf("%lld %lld",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%lld",&arr[0][i]);
        
        for(i=1;i<=n;i++)
            scanf("%lld",&arr[1][i]);
        arr[0][n+1]=arr[0][1];
        arr[1][n+1]=arr[1][1];
        if(n==1){
            if(arr[0][1]+arr[1][1]<=m) printf("1\n");
            else printf("2\n");
            continue;
        }
 
        dp[0][1][1]=1;
        dp[0][2][1]=1;
        if(arr[0][1]+arr[1][1]<=m) dp[0][3][1]=1;
        else dp[0][3][1]=2;
        
        for(i=2;i<=n;i++){
            dp[0][1][i]=min(dp[0][1][i],dp[0][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[0][1][i]=min(dp[0][1][i],dp[0][2][i-1]+1);
            
            dp[0][2][i]=min(dp[0][2][i],dp[0][3][i-1]+1);
            if(arr[1][i-1]+arr[1][i]<=m) dp[0][2][i]=min(dp[0][2][i],dp[0][1][i-1]+1);
            
            dp[0][3][i]=min(dp[0][3][i],dp[0][3][i-1]+2);
            if(arr[0][i]+arr[1][i]<=m) dp[0][3][i]=min(dp[0][3][i],dp[0][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[0][3][i]=min(dp[0][3][i],dp[0][2][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m) dp[0][3][i]=min(dp[0][3][i],dp[0][1][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m&&arr[0][i-1]+arr[0][i]<=m) dp[0][3][i]=min(dp[0][3][i],dp[0][3][i-2]+2);
        }
 
        dp[1][2][1]=1;
        dp[1][1][2]=2;
        dp[1][2][2]=2;
        if(arr[1][2]+arr[1][1]<=m){
            dp[1][2][2]=1;
            dp[1][3][2]=2;
        }
        if(arr[1][2]+arr[0][2]<=m){
            dp[1][3][2]=2;
        }
        dp[1][3][2]=min(dp[1][3][2],(ll)3);
        k=1;
        for(i=3;i<=n+1;i++){
            dp[k][1][i]=min(dp[k][1][i],dp[k][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[k][1][i]=min(dp[k][1][i],dp[k][2][i-1]+1);
            
            dp[k][2][i]=min(dp[k][2][i],dp[k][3][i-1]+1);
            if(arr[1][i-1]+arr[1][i]<=m) dp[k][2][i]=min(dp[k][2][i],dp[k][1][i-1]+1);
            
            dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-1]+2);
            if(arr[0][i]+arr[1][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][2][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][1][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m&&arr[0][i-1]+arr[0][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-2]+2);
        }
 
        dp[2][1][1]=1;
        dp[2][2][2]=2;
        dp[2][1][2]=2;
        if(arr[0][2]+arr[0][1]<=m){
            dp[2][1][2]=1;
            dp[2][3][2]=2;
        }
        if(arr[1][2]+arr[0][2]<=m){
            dp[2][3][2]=2;
        }
        dp[2][3][2]=min(dp[2][3][2],(ll)3);
        k=2;
        for(i=3;i<=n+1;i++){
            dp[k][1][i]=min(dp[k][1][i],dp[k][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[k][1][i]=min(dp[k][1][i],dp[k][2][i-1]+1);
            
            dp[k][2][i]=min(dp[k][2][i],dp[k][3][i-1]+1);
            if(arr[1][i-1]+arr[1][i]<=m) dp[k][2][i]=min(dp[k][2][i],dp[k][1][i-1]+1);
            
            dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-1]+2);
            if(arr[0][i]+arr[1][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][2][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][1][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m&&arr[0][i-1]+arr[0][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-2]+2);
        }
 
        dp[3][1][2]=1;
        dp[3][2][2]=1;
        if(arr[0][2]+arr[1][2]<=m) dp[3][3][2]=1;
        else dp[3][3][2]=2;
        
        k=3;
        for(i=3;i<=n+1;i++){
            dp[k][1][i]=min(dp[k][1][i],dp[k][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[k][1][i]=min(dp[k][1][i],dp[k][2][i-1]+1);
            
            dp[k][2][i]=min(dp[k][2][i],dp[k][3][i-1]+1);
            if(arr[1][i-1]+arr[1][i]<=m) dp[k][2][i]=min(dp[k][2][i],dp[k][1][i-1]+1);
            
            dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-1]+2);
            if(arr[0][i]+arr[1][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-1]+1);
            if(arr[0][i-1]+arr[0][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][2][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][1][i-1]+2);
            if(arr[1][i-1]+arr[1][i]<=m&&arr[0][i-1]+arr[0][i]<=m) dp[k][3][i]=min(dp[k][3][i],dp[k][3][i-2]+2);
        }
 
        printf("%lld\n",min({dp[0][3][n],dp[3][3][n+1],dp[1][1][n+1],dp[2][2][n+1]}));
    }
}
cs

'코딩 > 백준 문제 풀이' 카테고리의 다른 글

BOJ 18292 - NM과 K (2)  (0) 2020.02.29
BOJ 17955 - Max or Min  (0) 2020.02.17
BOJ 1520 - 내리막 길  (0) 2020.01.24
BOJ 4243 - 보안 업체  (0) 2019.12.03
BOJ 1848 - 동굴 탐험  (0) 2019.11.25