题面
太长了,不想粘,就甩个链接吧
题解
这个题就是转移方程的分类讨论麻烦了些,其他的没什么。
首先,我们要利用 Floyd 求出每两个点之间的距离 (n的范围那么小,肯定是Floyed 啦)
我们就要考虑怎么 dp
我们设 (f[i][j][0/1]) 表示 处理完前 (i) 门课,用了 (j) 次申请,且最后一门课换或者没换的概率。
然后我们就要开始大力讨论。
1. 这次上课没换的期望值
1.上次也没换,期望值就是 f[i-1][j][0] + dis[a[i-1]][[a[i]]
2.上次换了但没通过,他的概率是 1-p[i-1] 期望值就是 dis[a[i-1]][a[i]] * (1-p[i-1])
3.上次换了且通过了, 他的概率是 p[i-1] 期望值就是 p[i-1] * dis[b[i-1]][a[i]]
第二种和第三种情况和在一起就是 f[i-1][j][1] + p[i-1] * dis[b[i-1]][a[i]] + (1-p[i-1]) * dis[a[i-1]][a[i]]
这样,我们就得到了这次不换的转移方程
f[i][j][0] = min(f[i-1][j][0] + 1.0 * dis[a[i-1]][a[i]], f[i-1][j][1] + p[i-1] * dis[b[i-1]][a[i]] + (1-p[i-1]) * dis[a[i-1]][a[i]]);
2.这次要换的期望
上次没换,这次换了没通过的情况 他的概率是 (1-p[i]) 期望就是 (1-p[i]) * dis[a[i-1]][a[i]]
上次没换,这次换了通过的情况,他的概率是 p[i],期望值是 p[i] * dis[a[i-1]][b[i]]
上次换了没通过,这次换了也没通过的情况,概率为 (1-p[i-1]) * (1-p[i]) 期望值就是 (1-p[i-1]) * (1-p[i]) * dis[a[i-1]][a[i]]
上次换了没通过,这次换了通过的情况,概率为 (1-p[i-1]) * p[i] 期望值为 (1-p[i-1]) * p[i] * dis[a[i-1]][b[i]]
上次换了通过了,这次换了没通过的情况,概率为 p[i-1] * (1-p[i]) 期望值为 p[i-1] * (1-p[i]) * dis[b[i-1]][a[i]]
上次换了通过了,这次换了也通过的情况,概率为 p[i-1] * p[i] 期望值为 p[i] * p[i-1] * dis[b[i-1]][b[i]]
然后,前两种情况可以合并在一起由 f[i-1][j-1][0] 转移过来,后四种情况可以合并在一起,由 f[i-1][j-1][1] 转移过来。
具体方程长这样
f[i][j][1] = min(f[i-1][j-1][0] + p[i] * dis[a[i-1]][b[i]] + (1-p[i]) * dis[a[i-1]][a[i]],
f[i-1][j-1][1] + p[i] * p[i-1] * dis[b[i-1]][b[i]] + (1-p[i-1]) * (1-p[i]) * dis[a[i-1]][a[i]] + p[i-1] * (1-p[i]) * dis[b[i-1]][a[i]] + (1-p[i-1]) * p[i] * dis[a[i-1]][b[i]]);
初始化
f[1][0][0] = f[1][1][1] = 0.0, 其他都为极大值。
因为 1 是起点,从那边开始都是一样的
转移时注意 (j) 的边界 要特判一下 j == 0 的情况,我就因为这个交了好几遍
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,v,e,x,y,z,w,a[2010],b[2010],dis[310][310];
double p[2010],f[2010][2010][2],ans = 2333333.0;
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
int main()
{
n = read(); m = read(); v = read(); e = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) b[i] = read();
for(int i = 1; i <= n; i++) scanf("%lf",&p[i]);
for(int i = 1; i <= v; i++)
{
for(int j = 1; j <= v; j++)
{
if(i == j) dis[i][j] = 0;
else dis[i][j] = 23333333;
}
}
for(int i = 1; i <= e; i++)
{
x = read(); y = read(); w = read();
dis[x][y] = min(dis[x][y],w);//解决重边问题
dis[y][x] = min(dis[y][x],w);
}
for(int k = 1; k <= v; k++)//求距离
{
for(int i = 1; i <= v; i++)
{
for(int j = 1; j <= v; j++)
{
dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= m; j++)
{
f[i][j][0] = f[i][j][1] = 2333333.0;
}
}
f[1][0][0] = f[1][1][1] = 0.0;
for(int i = 2; i <= n; i++)
{
for(int j = 0; j <= m; j++)
{
f[i][j][0] = min(f[i-1][j][0] + 1.0 * dis[a[i-1]][a[i]], f[i-1][j][1] + p[i-1] * dis[b[i-1]][a[i]] + (1-p[i-1]) * dis[a[i-1]][a[i]]);
if(j != 0) f[i][j][1] = min(f[i-1][j-1][0] + p[i] * dis[a[i-1]][b[i]] + (1-p[i]) * dis[a[i-1]][a[i]],f[i-1][j-1][1] + p[i] * p[i-1] * dis[b[i-1]][b[i]] + (1-p[i-1]) * (1-p[i]) * dis[a[i-1]][a[i]] + p[i-1] * (1-p[i]) * dis[b[i-1]][a[i]] + (1-p[i-1]) * p[i] * dis[a[i-1]][b[i]]);
}
}
for(int i = 0; i <= m; i++) ans = min(ans,min(f[n][i][1],f[n][i][0]));//他申请次数可以不用完
printf("%.2lf",ans);
return 0;
}