理解题意:给定你一个学期的课程和教室数量以及教室之间的距离还有换教室成功的概率,求一个学期走的距离的期望最小值
题目是有够恶心的,属于那种一看就让人不想刷的题目。。。很明显的动规,但是那个动规方程真的是感人肺腑。。。绝对是我见过的最长的动规转移方程。。
状态还是非常好理解的,dp[ i ][ j ][ k ]表示到第 i 个时间段换了 j 次教室,第 j 次成功与否的最小期望路径,当然这里还需要用最短路优化(不用看,这么小的数据范围肯定是floyd),还是看代码吧,没什么好讲的,把那个长得感人肺腑的动规方程理解的就好做了:
#include<bits/stdc++.h> #define maxx 305 #define maxn 2012 using namespace std; int n,m,v,e,c1[maxn],c2[maxn]; double dp[maxn][maxn][2],ans; double f[maxx][maxx],p[maxn]; int main() { cin>>n>>m>>v>>e;ans=0x3f3f3f3f; for(int i=1;i<=n;i++)scanf("%d",&c1[i]); for(int i=1;i<=n;i++)scanf("%d",&c2[i]); for(int i=1;i<=n;i++)scanf("%lf",&p[i]); for(int i=1;i<=v;i++) for(int j=1;j<i;j++) { f[i][j]=0x3f3f3f3f; f[j][i]=0x3f3f3f3f; } for(int i=1;i<=e;i++){ int x,y;double w; scanf("%d%d%lf",&x,&y,&w); f[x][y]=min(f[x][y],w); f[y][x]=f[x][y];}//别忘了判重边 for(int k=1;k<=v;k++) for(int i=1;i<=v;i++) for(int j=1;j<i;j++) if(f[i][j]>f[i][k]+f[k][j]){ f[i][j]=f[i][k]+f[k][j]; f[j][i]=f[i][j];}//floyd,不解释 for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=1;k++) dp[i][j][k]=0x3f3f3f3f; dp[1][0][0]=0.0;dp[1][1][1]=0.0; for(int i=2;i<=n;i++){ for(int j=0;j<=i&&j<=m;j++){ dp[i][j][0]=min(dp[i-1][j][0]+f[c1[i-1]][c1[i]],//状态:上一次不换,这一次不换 dp[i-1][j][1]+f[c2[i-1]][c1[i]]*p[i-1] //上一次换成功 +f[c1[i-1]][c1[i]]*(1.0-p[i-1])); //上一次失败 if(j>=1) dp[i][j][1]=min(dp[i-1][j-1][0]+f[c1[i-1]][c2[i]]*p[i] //上一次不换,这一次换成功 +f[c1[i-1]][c1[i]]*(1.0-p[i]), //这一次失败 dp[i-1][j-1][1]+f[c2[i-1]][c2[i]]*p[i-1]*p[i] //上一次换,两次都成功 +f[c1[i-1]][c2[i]]*(1.0-p[i-1])*p[i] //上一次失败,这一次成功 +f[c2[i-1]][c1[i]]*p[i-1]*(1.0-p[i]) //上一次成功,这一次失败 +f[c1[i-1]][c1[i]]*(1.0-p[i-1])*(1.0-p[i])); //两次都失败 } }
//是不是长得感人肺腑??? for(int i=0;i<=m;i++) for(int j=0;j<=1;j++) ans=min(ans,dp[n][i][j]);
//找到最小期望路径 printf("%.2lf",ans); return 0; }
怎么样,是不是感人肺腑??QWQ