「NOIP 2016」換教室
Description
有 \(n\) 節課,第 \(i\) 節課可以選擇在教室 \(C_i\) 上課,也可以選擇申請換教室,到教室 \(D_i\) 上課。若選擇申請換課,則有 \(K_i\) 的概率申請通過,另外 \(1-K_i\) 的概率依然留在 \(C_i\) 教室。
共有 \(v\) 個教室,\(e\) 條路徑雙向連通教室 \(X_i\) 和 \(Y_i\),邊權為 \(W_i\)。在每兩節課之間,你要從上一個教室走到下一個教室。
最多提交 \(m\) 個申請,並且得在第一節課上課之前一次性提交所有申請(也就是,不能根據某一次申請的通過情況來選擇下一次申請),要使自己走的路的期望最小。求最優策略下的總距離的期望。
\(1\leq n\leq 2000,0\leq m \leq 2000,1\leq v\leq 300,0\leq e\leq 9\times 10^4,1\leq W_i \leq 1000,0\leq K_i\leq 1\)
Solution
考慮 DP,用 \(dp[i][j][0/1]\) 表示前 \(i\) 節課申請了 \(j\) 次,第 \(i\) 次是否申請的期望最小值。
先用 Floyd 預處理出兩個教室之間的最短路長度 \(dis(i,j)\)。
\(dp[i][j][0]\) 所記錄的狀態一定處在教室 \(c[i]\),\(dp[i][j][1]\) 所記錄的狀態有 \(k[i]\) 的概率處在 \(d[i]\)
討論第 \(i\) 次是否申請。\(P_1,P_2\) 是臨時變數,分別存第 \(i-1\) 節課“申請/不申請”兩種方案的期望總距離。
若不申請:
\(P_1=dp[i-1][j][0]+dis(c[i-1],c[i])\)
\(P_2=dp[i-1][j][1]+k[i-1]\times dis(d[i-1],c[i])+(1-k[i-1])\times dis(c[i-1],c[i])\)
則 \(dp[i][j][0]=min(P_1,P_2)\)
即在兩種策略中選取期望值較小的。
若申請:
\(P_1=dp[i-1][j-1][0]+k[i]\times dis(c[i-1],d[i])+(1-k[i])\times dis(c[i-1],c[i])\)
\(P_2=dp[i-1][j-1][1]+(1-k[i-1])\times k[i]\times dis(c[i-1],d[i])\\+(1-k[i-1])\times (1-k[i])\times dis(c[i-1],c[i])\\+k[i-1]\times k[i]\times dis(d[i-1],d[i])\\+k[i-1]\times (1-k[i])\times dis(d[i-1],c[i])\)
則 \(dp[i][j][1]=min(P_1,P_2)\)
#include<bits/stdc++.h> #define int long long using namespace std; const int N=2010,M=310,inf=1e18; int n,m,v,e,c[N],d[N],x,y,z,f[M][M]; double k[N],dp[N][N][2],p1,p2,ans=inf; signed main(){ scanf("%lld%lld%lld%lld",&n,&m,&v,&e); for(int i=1;i<=n;i++) scanf("%lld",&c[i]); for(int i=1;i<=n;i++) scanf("%lld",&d[i]); for(int i=1;i<=n;i++) scanf("%lf",&k[i]); //初始化兩點之間的最短路 for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) f[i][j]=f[j][i]=inf; for(int i=1;i<=e;i++){ scanf("%lld%lld%lld",&x,&y,&z); f[x][y]=f[y][x]=min(f[x][y],z); } for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) f[i][i]=0; //floyd 求任意兩點之間的最短路 for(int k=1;k<=v;k++) for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]); //初始化期望 for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) dp[i][j][0]=dp[i][j][1]=inf; //DP 之前的初值概率均為無窮大便於比較 //DP 計算期望 dp[1][0][0]=dp[1][1][1]=0; //DP 初值 for(int i=2;i<=n;i++) for(int j=0;j<=min(i,m);j++){ p1=dp[i-1][j][0]+f[c[i-1]][c[i]],p2=dp[i-1][j][1]+k[i-1]*f[d[i-1]][c[i]]+(1-k[i-1])*f[c[i-1]][c[i]]; dp[i][j][0]=min(dp[i][j][0],min(p1,p2)); if(j){ p1=dp[i-1][j-1][0]+k[i]*f[c[i-1]][d[i]]+(1-k[i])*f[c[i-1]][c[i]]; p2=dp[i-1][j-1][1]+(1-k[i-1])*k[i]*f[c[i-1]][d[i]]+(1-k[i-1])*(1-k[i])*f[c[i-1]][c[i]]+k[i-1]*k[i]*f[d[i-1]][d[i]]+k[i-1]*(1-k[i])*f[d[i-1]][c[i]]; dp[i][j][1]=min(dp[i][j][1],min(p1,p2)); } } //計算答案 for(int i=0;i<=m;i++) ans=min(ans,min(dp[n][i][0],dp[n][i][1])); printf("%.2lf\n",ans); return 0; }