题目大意:有n个时间段,每个时间段i有两个教室a[i],b[i]可以上课,如果不申请换教室就在教室a[i]上课,如果换教室就在b[i]上课。你最多只能换m次教室。教室之间有一些双向路,保证教室两两可以到达。问上完n节课走路长度的数学期望。
题解:这是一道典型的概率dp题。重要的是状态的设计。
我们令dp[i][j][0/1]代表:第i个时间段换了j次课室这次换(0)还是不换(1)的数学期望。需要注意的是因为要满足dp的无后效性,必须要加上dp状态的第三维代表这一次还还是不换。
根据上次换还是不换和这次换还是不换, 那么就有4种结果。
dp[i][j][0]=min(dp[i-1][j][0]+上次和这次都不换的期望代价,dp[i-1][j][1]+上次换这次不换的期望代价);
dp[i][j][1]=min(dp[i-1][j-1][0]+上次不换这次换的期望代价,dp[i-1][j-1][1]+上次和这次都换的期望代价);
dp边界为dp[1][0][0]=dp[1][0][1]=0; 答案为在i==n中取最小值。
这道题还是有一些细节(期望代价的计算)得看代码的。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2000+10; const int V=300+10; const int INF=0x3f3f3f3f; int n,m,v,e; int a[N],b[N],map[V][V]; double k[N],dp[N][N][2]; void floyd() { for (int k=1;k<=v;k++) for (int i=1;i<=v;i++) for (int j=1;j<=v;j++) map[i][j]=min(map[i][j],map[i][k]+map[k][j]); } int main() { scanf("%d%d%d%d",&n,&m,&v,&e); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) scanf("%d",&b[i]); for (int i=1;i<=n;i++) scanf("%lf",&k[i]); memset(map,0x3f,sizeof(map)); for (int i=1;i<=v;i++) map[i][i]=0; for (int i=1;i<=e;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); if (map[x][y]>z) map[x][y]=z; if (map[y][x]>z) map[y][x]=z; } floyd(); for (int i=1;i<=n;i++) for (int j=0;j<=m;j++) dp[i][j][0]=dp[i][j][1]=INF; dp[1][0][0]=0; dp[1][1][1]=0; double ans=INF; for (int i=2;i<=n;i++) for (int j=0;j<=m;j++) { double w1=map[a[i-1]][a[i]],w2=map[a[i-1]][b[i]]; double w3=map[b[i-1]][a[i]],w4=map[b[i-1]][b[i]]; dp[i][j][0]=min(dp[i-1][j][0]+w1,dp[i-1][j][1]+w1*(1-k[i-1])+w3*k[i-1]); double temp=w1*(1-k[i-1])*(1-k[i])+w2*(1-k[i-1])*(k[i])+w3*(k[i-1])*(1-k[i])+w4*(k[i-1])*(k[i]); if (j) dp[i][j][1]=min(dp[i-1][j-1][0]+w1*(1-k[i])+w2*(k[i]),dp[i-1][j-1][1]+temp); if (i==n) ans=min(ans,min(dp[i][j][0],dp[i][j][1])); } printf("%.2lf",ans); return 0; }