코딩/백준 문제 풀이

BOJ 17955 - Max or Min

stonejjun 2020. 2. 17. 02:36

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

나한테 잘 맞는 문제였다. 정말 좋은 문제라고 생각한다.

문제 소개

n,m이 주어진다. 이후 1 이상 m 이하의 수 n개가 주어진다. n개의 숫자는 원형으로 이루어져 있다. 이후 2가지 행동을 할 수 있다.
1. 어떤 한 칸을 골라 그 수를 max(원래 숫자, 왼쪽의 숫자, 오른쪽 숫자)로 바꾼다.
2. 어떤 한 칸을 골라 그 수를 min(원래 숫자, 왼쪽의 숫자, 오른쪽 숫자)로 바꾼다.

원형으로 이루어진 n개의 숫자를 모두 (1,2,3,...m)으로 만드는데 필요한 최소 행동의 횟수를 각각 구하시오. 만약 만들 수 없다면 -1을 출력한다.

문제 풀이 

스포 주의!

Step1. 문제 관찰

문제의 예제를 봄으로써 몇가지 사실을 관찰 할 수 있다.

1. 배열에 존재하지 않는 수는 절대로 만들 수 없다. (-1이 나온다.)
2. 배열에 존재하는 수는 무조건 만들 수 있다. 
3. 어떤 연속된 세개의 숫자에 대해서 k가 중간 값이 아니라면 가운데 숫자를 1번의 행동으로 k로 만들 수 있다.
4. 최댓값 최솟값에 집중을 해보자. 최댓값은 나머지 모두를 1번 행동을 함으로써 완성시킬 수 있고, 최솟값은 나머지 모두를 2번 행동을 함으로써 완성시킬 수 있다.
5-1. 그 밖의 숫자에 주목을 해보자. 4에 해당하지 않는 숫자 k가 있을 때, 나머지 모두를 1번행동을 한 뒤, 2번 행동을 하면 모두 k로 만들 수 있다.
5-2. k보다 큰 숫자에 대해서는 1번 행동을 하지 않아도 된다.
5-3. 이와 반대의 경우에도 성립한다. (2번 행동 후 1번행동)
6. 따라서 어떤 k에 대해서 현재 구할 수 있는 답은 최대 min(k보다 큰 숫자의 갯수, k보다 작은 숫자의 갯수)+k가 아닌 숫자의 갯수 이다. 

나는 위와 같은 관찰을 하였다. 이 관찰을 모두 하는 것은 분명히 쉽지 않다. 하지만, 이 부분이 나의 강점이다. 직접 상황을 만들어보고 계속 시행을 해보고 예제를 연구하면서 관찰을 할 수 있었다. 

여기까지의 관찰을 토대로 좀 더 세밀화 된 상황을 만들고 추가적인 관찰을 해야한다. 여기까지 글을 읽었다면 아래의 내용을 읽기 전에 좀 더 생각하는 시간을 가져보는 것을 추천한다. 바로 남의 풀이과정을 따라가기에는 좀 아까운 문제이다. 

Step 2. 추가 관찰+ 풀이 기초

일단 관찰 4,5를 통해서 k보다 큰 숫자와 작은 숫자만 의미가 있다는 것을 알았다. 따라서 1,2,3만 있는 상황을 생각해보자. 4에 의해서 1,3에 대해서는 구하기 쉽다. 하지만, F(2)를 구하는 것이 이 상황의 목적이다. 두가지 상황에 대한 값을 생각해보면 6번에서 갯수가 아닌 다른 것에 영향을 받는다는 사실을 알 수 있다.

Case 1:  2 1 1 3 3  
a2에 max 취하기 a5에 min 취하기 a3에 max 취하고 min취하기 a4에 min 취하기 -> 5회

Case 2:  2 1 3 1 3  
a2에 max 취하고 min 취하기 a4에 max 취하기 a5에 min 취하기 a3에 min 취하기 a4에 min취하기 -> 6회

Case 3:  2 1 3 3 1  
a2에 max 취하고 min 취하기  a3에 min 취하기 a4에 max 취하기 a5에 min 취하기 a4에 min취하기 -> 6회

이러한 상황들을 다시 관찰하고 3번 관찰을 보자. 뭔가 떠오르는 것이 생긴다. 연속된 1은 한쪽 끝이 2이면 계속 max를 취해주면서 쭉 2로 만들 수 있다. 3의 경우도 마찬가지. 여기서 중요한 것은 1,3 이 연속되어있을 때라는 것을 알 수 잇다. 이때 두 개를 둘 다 2로 만들기 위해서는 3번의 작업이 필요하다.

여기서 주의할 점은 1번의 (1,3) 당 한번의 추가 작업이 아니라 (1,3)을 합쳐서 3번이라는 것이다. 이 때문에 Case 2는 7번이 아닌 6번이 되는 것이다. 

따라서 최종적으로 결론을 내자면 F(k)를 구하기 위해서는 k의 갯수와 (k보다 큰 숫자, k보다 작은 숫자)의 묶음을 구하면 된다. 

Step 3. 풀이 세분화  + 구현방식 생각

어떤 k에 대해서 그룹을 몇개 만들어 줄 수 있는지에 대해서 세는 것이 아니라 배열의 인접한 두 원소를 잡고 그 사이의 값을 업데이트 하는 느낌으로 해결할 수 있다. (a1,a2)를 업데이트 하고 (a2,a3)-(a1,a2) (집합에서 빼는 형식) 을 업데이트하고 ...  마지막에 (an-1,an)-(an-2,an-1)-(a1,an)을 업데이트하면 연속해서 두번을 업데이트 하는 경우가 존재하지 않는다. 

말로하면 이해가 어렵기 때문에 예제를 예시로 들려고 한다.

(2,5) -> [3,4]  0 0 1 1 0
(1,5)-(2,5) ->[2,4]-[3,4] ->[2,2]  0 1 1 1 0
(1,1) -> X  0 1 1 1 0
(1,2) -> X 0 1 1 1 0
(2,3) -> X 0 1 1 1 0
(2,3) -> X 0 1 1 1 0
(2,2) -> X 0 1 1 1 0

F(1)=7-2+0=5 F(2)=7-3+1=5 F(1)=7-1+1=7 F(4)=-1 F(5)=7-1+0=6

위와 같은 방식으로 답이 도출되는 것을 확인할 수 있다. 구현은 Range Upate Point Query이므로 Segment Tree with Lazy propagation을 짜주면 될 것이다. 

여기서 한 가지 문제가 생기게 되는데, 이는 첫 구간을 항상 사용한다고 했기 때문에 어떤 숫자에 대한 최대 그룹의 갯수가 나오지 않을 수 있다. 따라서 한칸씩 돌린 후 위의 과정을 한 번더 진행해준후 max값을 뽑아내면 된다.

코드

 

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
 
ll arr[1010101];
ll arr2[1010101];
ll cnt[1010101];
ll ans[1010101];
ll ans2[1010101];
 
 
struct node{
    ll val,laz;
};
node tree[5101010];
 
void upt(ll l,ll r,ll s,ll e,ll nod){
    //printf("%lld %lld %lld %lld %lld\n",l,r,s,e,nod);
    if(tree[nod].laz!=0){
        tree[nod].val+=tree[nod].laz;
        tree[nod*2].laz+=tree[nod].laz;
        tree[nod*2+1].laz+=tree[nod].laz;
        tree[nod].laz=0;
    }
    if(r<s||e<l) return ;
    if(l<=s&&e<=r){
        tree[nod].laz++;
        return ;
    }
    upt(l,r,s,(s+e)/2,nod*2);
    upt(l,r,(s+e)/2+1,e,nod*2+1);
}
 
ll sol(ll idx,ll s,ll e,ll nod){
    if(tree[nod].laz!=0){
        tree[nod].val+=tree[nod].laz;
        tree[nod*2].laz+=tree[nod].laz;
        tree[nod*2+1].laz+=tree[nod].laz;
        tree[nod].laz=0;
    }
    if(idx<s||idx>e) return 0;
    if(s==e) return tree[nod].val;
    return sol(idx,s,(s+e)/2,nod*2)+sol(idx,(s+e)/2+1,e,nod*2+1);
}
 
node tree2[3101010];
 
void upt2(ll l,ll r,ll s,ll e,ll nod){
    if(tree2[nod].laz!=0){
        tree2[nod].val+=tree2[nod].laz;
        tree2[nod*2].laz+=tree2[nod].laz;
        tree2[nod*2+1].laz+=tree2[nod].laz;
        tree2[nod].laz=0;
    }
    if(r<s||e<l) return ;
    if(l<=s&&e<=r){
        tree2[nod].laz++;
        return ;
    }
    upt2(l,r,s,(s+e)/2,nod*2);
    upt2(l,r,(s+e)/2+1,e,nod*2+1);
}
 
ll sol2(ll idx,ll s,ll e,ll nod){
    if(tree2[nod].laz!=0){
        tree2[nod].val+=tree2[nod].laz;
        tree2[nod*2].laz+=tree2[nod].laz;
        tree2[nod*2+1].laz+=tree2[nod].laz;
        tree2[nod].laz=0;
    }
    if(idx<s||idx>e) return 0;
    if(s==e) return tree2[nod].val;
    return sol2(idx,s,(s+e)/2,nod*2)+sol2(idx,(s+e)/2+1,e,nod*2+1);
}
 
int main(){
    cin.tie(0);
    ios_base::sync_with_stdio(0);
    ll i,j,k,l,m,n;
    cin>>n>>m;
    for(i=1;i<=n;i++){
        cin>>arr[i];
        cnt[arr[i]]++;
    }
    arr[n+1]=arr[1];
    arr[n+2]=arr[2];
    for(i=1;i<=n+1;i++)
        arr2[i]=arr[i+1];
 
    ll fs,fe,ls,le,ns,ne;
    fs=ls=min(arr[1],arr[2])+1;
    fe=le=max(arr[1],arr[2])-1;
 
    upt(ls,le,1,m,1);
    for(i=3;i<=n;i++){
        ns=min(arr[i],arr[i-1])+1;
        ne=max(arr[i],arr[i-1])-1;
        if(ne<ns){
            ;
        }
        else if(ls<=ns&&ne<=le){
            ns=1e18;
            ne=0;
        }
        else if(le<ns||ne<ls){
            ;
        }
        else if(le==ne&&ns<ls){
            ne=ls-1;
        }
        else if(ls==ns&&le<ne){
            ns=le+1;
        }
        //printf("%lld %lld %lld\n",i,ns,ne);
        upt(ns,ne,1,m,1);
        ls=ns;
        le=ne;
    }
    i=n+1;
    ns=min(arr[i],arr[i-1])+1;
    ne=max(arr[i],arr[i-1])-1;
    if(ne<ns){
        ;
    }
    else if(ls<=ns&&ne<=le){
        ns=1e18;
        ne=0;
    }
    else if(le<ns||ne<ls){
        ;
    }
    else if(le==ne&&ns<ls){
        ne=ls-1;
    }
    else if(ls==ns&&le<ne){
        ns=le+1;
    }
 
    if(ne<ns){
        ;
    }
    else if(fs<=ns&&ne<=fe){
        ns=1e18;
        ne=0;
    }
    else if(fe<ns||ne<fs){
        ;
    }
    else if(fe==ne&&ns<fs){
        ne=fs-1;
    }
    else if(fs==ns&&fe<ne){
        ns=fe+1;
    }
    upt(ns,ne,1,m,1);
 
 
 
    fs=ls=min(arr2[1],arr2[2])+1;
    fe=le=max(arr2[1],arr2[2])-1;
 
    upt2(ls,le,1,m,1);
    for(i=3;i<=n;i++){
        ns=min(arr2[i],arr2[i-1])+1;
        ne=max(arr2[i],arr2[i-1])-1;
        if(ne<ns){
            ;
        }
        else if(ls<=ns&&ne<=le){
            ns=1e18;
            ne=0;
        }
        else if(le<ns||ne<ls){
            ;
        }
        else if(le==ne&&ns<ls){
            ne=ls-1;
        }
        else if(ls==ns&&le<ne){
            ns=le+1;
        }
        upt2(ns,ne,1,m,1);
        ls=ns;
        le=ne;
    }
    i=n+1;
    ns=min(arr2[i],arr2[i-1])+1;
    ne=max(arr2[i],arr2[i-1])-1;
    if(ne<ns){
        ;
    }
    else if(ls<=ns&&ne<=le){
        ns=1e18;
        ne=0;
    }
    else if(le<ns||ne<ls){
        ;
    }
    else if(le==ne&&ns<ls){
        ne=ls-1;
    }
    else if(ls==ns&&le<ne){
        ns=le+1;
    }
 
    if(ne<ns){
        ;
    }
    else if(fs<=ns&&ne<=fe){
        ns=1e18;
        ne=0;
    }
    else if(fe<ns||ne<fs){
        ;
    }
    else if(fe==ne&&ns<fs){
        ne=fs-1;
    }
    else if(fs==ns&&fe<ne){
        ns=fe+1;
    }
    upt2(ns,ne,1,m,1);
 
    for(i=1;i<=m;i++){
        if(cnt[i]==0printf("-1 ");
        else printf("%lld ",n-cnt[i]+max(sol(i,1,m,1),sol2(i,1,m,1) ));
 
        //printf("%lld %lld %lld %lld\n",i,cnt[i],sol(i,1,m,1),sol2(i,1,m,1));
    }
 
}
cs

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

BOJ 15647 - 로스팅하는 엠마도 바리스타입니다  (0) 2020.03.05
BOJ 18292 - NM과 K (2)  (0) 2020.02.29
BOJ 1006 - 습격자 초라기  (0) 2020.02.09
BOJ 1520 - 내리막 길  (0) 2020.01.24
BOJ 4243 - 보안 업체  (0) 2019.12.03