블로그 옮겼습니다
Codeforces Round #412 (Div. 2) E. Prairie Partition 본문
Codeforces Round #412 (Div. 2) E. Prairie Partition
sgc109 2017. 5. 9. 14:32그럼 결국 이 문제는 주어진 N개의 원소들로 하나도 남김없이 체인으로 합쳐 줄 때
몇개의 체인으로 합칠 수 있냐를 묻는 문제가 된다. 이 문제는 우선 그리디하게 풀 수 있다.
그럼 우선 문제를 구하는 방향은 이렇게 하겠다.
빈 체인 하나를 만들고 남은 원소들을 남김없이 빈 체인들에 쑤셔 넣을 수 있는지 확인한다. 되면 답에 1을 추가한다.
빈 체인 두개를 만들고 남은 원소들을 남김없이 빈 체인들에 쑤셔 넣을 수 있는지 확인한다. 되면 답에 2를 추가한다.
빈 체인 세개를 만들고 남은 원소들을 남김없이 빈 체인들에 쑤셔 넣을 수 있는지 확인한다. 되면 답에 3을 추가한다.
........
빈 체인을 더이상 만들 수 없을 때 까지 반복한다.
그럼 답을 모두 출력하면된다. 만약 답이 없다면 -1을 출력하면 된다.
그럼 이제 빈 체인을 m개를 만들 때 어떤 기준으로 빈 체인을 만들어야 하는가, 그리고
어떤 방법으로 남은 원소들을 빈 체인들에 쑤셔 넣을 것인가 만 알면된다.
우선 빈 체인을 m개 만드는 기준은 두가지가 있다. 우선 체인의 오른쪽 끝 수(가장 큰 수)가 클수록좋다.
왜냐하면 각각의 빈 체인은 2*(끝 수) 이하인 r만 합칠 수가 있기 때문에 끝 수가 클 수록
합칠 수 있는 r의 가짓수가 많아지기 때문이다. 두번 째로 체인에 포함되는 수가 많을 수록 좋다.
왜냐하면 남은 원소들을 빈체인들에 하나씩 모두 붙여줘야되는데 이 남은 원소들이 적을 수록
빈 체인들에 남김없이 다 붙여줄 가능성이 커지기 때문이다. 그런데 예를 들어
우선 빈 체인A와 빈 체인B가 있고 체인A의 길이는 lenA, B의 길이는 lenB 라면
lenA < lenB 이면 A에 포함되는 원소들의 집합은 B에 포함되는 원소들의 집합의 진 부분집합이다.
왜냐면 빈 체인은 무조건 1로시작해서 2배씩 증가하는 연속된 숫자들로 이루어져있기 때문이다.
그렇기 때문에 하나의 빈 체인을 만들 때 가능한한 현재 남은 원소들로 만들 수 있는 가장 긴 체인을 만드는 것이
이득이라는 뜻이 된다. 그렇게 되면 끝 수도 커지면서 체인에 포함되는 수도 많아지기 때문이다.
그러면 이제 체인을 m개 만들었다면 어떻게 남은 원소들을 체인에 붙여줄까
우선 남은 원소들 중 크기가 큰 원소일 수록 붙을 수 있는 빈 체인의 후보가 적을 것이다. 왜냐하면
빈 체인의 가장 큰 원소가 커야 하기 때문이다. 그럼 남은 원소들 중 가장 큰 원소부터 끝 수가 가장 큰
빈 체인에 우선으로 붙여나가면 될 것이다. 이 것을 코드로 어떻게 옮겨 넣을까?
한가지 유용한 사실 중 하나는 예를들어 5~8는 끝 수가 4이상인 빈 체인에만 붙을 수가 있기 때문에
끝 수가 큰 빈 체인부터 내려오면서 끝 수가 4인 빈 체인의 수까지 누적해왔다면
2까지 왔을 때 현재 가지고 있는 빈 체인의 수 보다 남은 수 중 5~8의 개수의 합이 더 많다면
지금 의 m에 대해 m개의 꽉 찬 체인은 만들 수가 없다는 뜻이 되고, 결과적으로
입력으로 주어진 sequence 는 m개의 수를 분해해서는 나올 수 없는 sequence 라는 말이 된다.
끝 수가 1인 빈 체인까지 이걸 반복하여 방금 말한 것처럼 사용가능한 빈체인의 수가 부족한 상황이 발생하지 않는다면
m개의 꽉찬 체인을 만들 수 있다는 것이다. 이걸 위에서 말했던 것처럼 빈 체인을 만들 수 없을 때까지 반복하면 된다.
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 | #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod = 1e9+7; const int inf = 0x3c3c3c3c; const long long infl = 0x3c3c3c3c3c3c3c3c; int N; ll A[100003]; int rs[53]; int chunk[53]; int pow2[53]; int main() { scanf("%d",&N); for(int i = 0 ; i < N; i++){ scanf("%lld",&A[i]); ll a = A[i]; int cnt = -1; for(ll b = a; b ; b/=2) cnt++; if(a == (a&-a)) pow2[cnt]++; else rs[cnt]++; } vector<int> ans; int cnt = 0; while(pow2[0]){ cnt++; int pos = 0; while(pow2[pos]) pow2[pos++]--; chunk[pos-1]++; int now = 0; int ok = 1; for(int i = 50; i >= 0 ; i--){ now += chunk[i]; now -= pow2[i+1] + rs[i]; if(now < 0){ ok = 0; break; } } if(now - pow2[0] < 0) continue; if(ok) ans.push_back(cnt); } if(ans.size()==0) return !printf("-1"); for(int i = 0 ; i < ans.size(); i++) printf("%d ",ans[i]); return 0; } | cs |
'Algorithm > Problem Solving' 카테고리의 다른 글
CSacademy Round #29 (Div. 2 only) D. Water Bottles (0) | 2017.05.11 |
---|---|
Topcoder SRM 514 Div1(Medium) MagicalGirlLevelTwoDivOne (0) | 2017.05.10 |
BOJ 1023번 괄호 문자열 (3) | 2017.05.09 |
BOJ 9426번 중앙값 측정 (0) | 2017.05.09 |
Topcoder SRM 714 Div1(Easy) Paranthesis Removal (0) | 2017.05.08 |