先日Div2 Hardを解いたので、その制限が難しい版。
http://community.topcoder.com/stat?c=problem_statement&pm=11508
問題
下記Div2 Hardを参照。
TopCoder SRM 515 Div2 Hard NewItemShopTwo - kmjp's blog
ただし、Div2 HardとDiv1 Mediumは以下の点が異なる。
- Div2 Hardは客は2人だが、こちらは客は最大24人である。
- Div2 Hardは売るものは1つしかないが、こちらはsword個ある。
それぞれの客が最大1回しか店に来ない場合、利益を最大化せよ。
解法
基本的な解法はDiv2 Hardと変わらない。
ただ、自分でさらっと再帰が書けなかったので一部他の解答を参考にした。
後ろの時間から順に、「残された客が来る確率」をもとにその時間に来た客にものを売るか売らないか最適手を取っていく。
ただし、test #0のように8時または16時に来る客がいる場合、12時の客の相手をどうするかは8時にすでに客が来たかどうかで決まる。
そこで、状態として[時間][残りsword数][以前に客が来たかのbitmap]を持つ。
客の数は最大24人いるが、bitmaskを持たなければいけないのは、2回以上来る客だけなので、最大12bitで良い。
あとは以下の処理を0時から再帰的に行う。結果的に後ろの時刻から最適手が確定していく。
- 今の時間に客が来ないなら、次の時間の結果を返す。
- bitmaskを見て、今の時間の客が来ないのが確定的(前の時間にすでに来ている)なら、次の時間の結果を返す。
int CC,NB; int N[51]; int B[51]; int T[51][51],C[51][51],IP[51][51]; double P[51][51],TP[51][51]; double dpdp[25][25][1<<12]; int TN[51],TID[51]; class NewItemShop { public: double dodo(int tim, int sw, int mask) { if(tim>=24 || sw<=0) return 0; if(dpdp[tim][sw][mask]>=0) return dpdp[tim][sw][mask]; if(TN[tim]==-1) return dpdp[tim][sw][mask] = dodo(tim+1, sw, mask); if(B[TN[tim]]>=0 && (mask & (1<<B[TN[tim]]))) return dpdp[tim][sw][mask] = dodo(tim+1, sw, mask); double happen=P[TN[tim]][TID[tim]]/(1-TP[TN[tim]][tim]); double ntp = dodo(tim+1, sw, mask); int nm=mask; if(B[TN[tim]]>=0) nm |= 1<<B[TN[tim]]; double take = 0; if(sw>0) take = C[TN[tim]][TID[tim]] + dodo(tim+1,sw-1,nm); double notake = dodo(tim+1,sw,nm); return dpdp[tim][sw][mask] = happen*max(take,notake) + (1-happen)*ntp; } double getMaximum(int swords, vector <string> customers) { int i,j,x,y,t; string s; CC=customers.size(); NB=0; ZERO(N);MINUS(B); ZERO(T);ZERO(C);ZERO(P);ZERO(IP); MINUS(TN),MINUS(TID);ZERO(TP); FOR(i,CC) { int d=0,j; N[i]=0; ITR(it,customers[i]) { if(*it==',') d++; else if(*it==' ') d=0,N[i]++; else if(d==0) T[i][N[i]]=T[i][N[i]]*10+*it-'0'; else if(d==1) C[i][N[i]]=C[i][N[i]]*10+*it-'0'; else if(d==2) IP[i][N[i]]=IP[i][N[i]]*10+*it-'0'; } N[i]++; FOR(j,N[i]) TN[T[i][j]]=i,TID[T[i][j]]=j,P[i][j]=IP[i][j]/100.0; FOR(j,N[i]) for(x=T[i][j]+1;x<=24;x++) TP[i][x] += P[i][j]; if(N[i]>1) B[i]=NB++; } FOR(i,25) FOR(j,25) FOR(x,1<<12) dpdp[i][j][x]=-1; return dodo(0,swords,0); } };
まとめ
わかりやすい問題設定だが、地味に面倒だ。