ややこしくはあるけど、難しくはない。
https://yukicoder.me/problems/no/2158
問題
Nグループの問題セットからなるコンテストを解くことを考える。
i番目のグループはP[i]問からなり、各問題初見ではS[i]分、2回目以降はT[i]分で解ける。
これからコンテストを繰り返し行う。
各コンテストでは、各グループから1問ずつ問題を等確率ランダムで選び、N問のコンテストを構成する。
ただし、各グループ直前K回で使った問題は使わないこととする。
N問を計60分で解き切ればそこで終了とする。
終了に至るまでのコンテスト回数の最小値・最大値・期待値を求めよ。
解法
各グループのうち解いたことのある問題数を状態として、DPであと何回コンテスト参加すれば解き切れるか、最小値・最大値・期待値を計算しよう。
以下のコードではN重ループを書いている。
Nごとにコードを書くのは面倒なので、Nが最大値でない場合、S[i]=T[i]=0である問題セットを追加し、Nが常に最大値になるようにしている。
int N,K; int P[55],S[55],T[55]; double dp[3][6][6][6][6][6][6]; int V[6]; void solve() { int i,j,k,l,r,x,y; string s; cin>>N>>K; FOR(i,N) cin>>P[i]>>S[i]>>T[i]; for(i=N;i<6;i++) P[i]=5; int sum=0; FOR(i,6) sum+=T[i]; if(sum>60) { cout<<-1<<endl; return; } sum=0; FOR(i,6) sum+=S[i]; if(sum<=60) { cout<<"1 1 1"<<endl; return; } for(V[0]=P[0];V[0]>=K;V[0]--) for(V[1]=P[1];V[1]>=K;V[1]--) for(V[2]=P[2];V[2]>=K;V[2]--) for(V[3]=P[3];V[3]>=K;V[3]--) for(V[4]=P[4];V[4]>=K;V[4]--) for(V[5]=P[5];V[5]>=K;V[5]--) { int mask; dp[0][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]]=1e9; dp[1][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]]=-1e9; dp[2][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]]=0; int V2[6]; FOR(mask,64) { double p=1; int sum=0; FOR(i,6) { if(mask&(1<<i)) { //new p=p*(P[i]-V[i])/(P[i]-K); sum+=S[i]; V2[i]=V[i]+1; } else { p=p*(V[i]-K)/(P[i]-K); sum+=T[i]; V2[i]=V[i]; } } if(p==0) continue; if(sum<=60) { dp[0][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]]=1; chmax(dp[1][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]],1.0); dp[2][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]]+=p; } else { chmin(dp[0][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]],dp[0][V2[0]][V2[1]][V2[2]][V2[3]][V2[4]][V2[5]]+1); chmax(dp[1][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]],dp[1][V2[0]][V2[1]][V2[2]][V2[3]][V2[4]][V2[5]]+1); dp[2][V[0]][V[1]][V[2]][V[3]][V[4]][V[5]]+=p*(dp[2][V2[0]][V2[1]][V2[2]][V2[3]][V2[4]][V2[5]]+1); } } } _P("%.12lf %.12lf %.12lf\n",dp[0][K][K][K][K][K][K]+K,dp[1][K][K][K][K][K][K]+K,dp[2][K][K][K][K][K][K]+K); }
まとめ
★3.5位に感じた。