GCJ 2015 Qual B. Infinite House of Pancakes
問題
https://code.google.com/codejam/contest/6224486/dashboard
人間が無限にいて、全員の前にその人の皿がおいてある。 初期状態で、そのうち $D$ 人の皿の上にはパンケーキが乗っていて、それ以外の人には無い。
あなたは 1 分毎に次の 2 つの操作のうちいずれかができる。
- 皿にパンケーキが乗っている人全員に食べさせる。各人は 1 枚だけ食べることができる
- 皿にパンケーキが乗っている人を 1 人選び、その人のパンケーキを任意の人の皿に任意の枚数だけ移す
全てのパンケーキを食べさせるのにかかる時間の最小値は?
方針
同時に食べる人数は多いほうが良いので、移動するならまとめて最初にやってしまったほうが良い。 また、分散させるならなるべく均等に分けたほうが良い。 移動の回数 $cut$ と各人が食べる数の最大値 $len$ を決め、そうすることが可能なら更新していった。
これで small は通る。
実装
bool check(int cut, int len, vector<int> const & P){
int rem = cut;
rep(i,P.size()){
int c = P[i]%len==0 ? P[i]/len-1 : P[i]/len;
if(rem < c) return false;
rem -= c;
}
return true;
}
int solve(int D, vector<int> P){
int ans1 = 1<<29;
#ifdef TEST
int ans2 = 1<<29;
#endif
int S = accumulate(all(P),0);
for(int cut = 0; cut <= S; cut++){
int len_l = 0;
int len_r = 1010;
int len_m;
while(len_l + 1 < len_r){
len_m = (len_l + len_r) / 2;
if(check(cut, len_m, P)){
len_r = len_m;
} else {
len_l = len_m;
}
}
ans1 = min(ans1, cut + len_r);
#ifdef TEST
for(int len = 1; len <= 1010; len++){
if(check(cut, len, P)){
ans2 = min(ans2, cut+len);
}
}
#endif
}
#ifdef TEST
if(ans1 != ans2){
dump(ans1, ans2);
dump(P);
abort();
}
#endif
return ans1;
}
signed main(){
int T;
cin >> T;
rep(i,T){
int D;
cin >> D;
vector<int> P(D);
rep(j,D) cin >> P[j];
int x = solve(D,P);
cout << "Case #" << i+1 << ": " << x << endl;
}
}