[bzoj4720] [noip2016]换教室
概率期望\(dp\).
首先随便用个啥处理出任意两点之间的最短路。
设\(f[i][j][0/1]\)表示前\(i\)个时间段申请换了\(j\)次教室的期望最小值,\(0/1\)记录第\(i\)次有没有换教室。
然后枚举前两维,分情况讨论下这次和上次有没有申请换教室,申请的有没有成功。
转移方程就很显然了。
\[f[i][j][0]=min(f[i-1][j][0]+dis[c[i-1]][c[i]],f[i-1][j][1]+dis[c[i-1]][c[i]]*(1.0-p[i-1])+dis[d[i-1]][c[i]]*p[i-1]);
\]
申请了换教室的情况也类似,方程太长就不写了。
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define read(x) scanf("%d",&x)
#define write(x) printf("%d\n",x)
#define maxn 2005
int n,m,v,e,c[maxn],d[maxn],mp[305][305];
double f[maxn][maxn][2],p[maxn];
int main(){
//freopen("testdata.in","r",stdin);
//freopen("out.out","w",stdout);
read(n),read(m),read(v),read(e);int x,y,z;
for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) mp[i][j]=1e9;
for(int i=1;i<=n;i++) read(c[i]);
for(int i=1;i<=n;i++) read(d[i]);
for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
for(int i=1;i<=e;i++) read(x),read(y),read(z),mp[x][y]=mp[y][x]=min(mp[x][y],z);
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<i;j++)
mp[i][j]=mp[j][i]=min(mp[i][j],mp[i][k]+mp[k][j]);
for(int i=1;i<=v;i++) mp[i][i]=mp[i][0]=mp[0][i]=0;
for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=1e9;
f[1][1][1]=f[1][0][0]=0;
for(int i=2;i<=n;i++){
f[i][0][0]=f[i-1][0][0]+mp[c[i-1]][c[i]];
for(int j=1;j<=min(i,m);j++){
f[i][j][0]=min(f[i-1][j][0]+mp[c[i-1]][c[i]],f[i-1][j][1]+mp[c[i-1]][c[i]]*(1.0-p[i-1])+mp[d[i-1]][c[i]]*p[i-1]);
f[i][j][1]=min(f[i-1][j-1][0]+mp[c[i-1]][c[i]]*(1.0-p[i])+mp[c[i-1]][d[i]]*p[i],
f[i-1][j-1][1]+mp[c[i-1]][c[i]]*(1.0-p[i-1])*(1.0-p[i])+mp[c[i-1]][d[i]]*(1.0-p[i-1])*p[i]+
mp[d[i-1]][c[i]]*p[i-1]*(1.0-p[i])+mp[d[i-1]][d[i]]*p[i-1]*p[i]);
}
}
double ans=1e17;
for(int i=0;i<=m;i++) ans=min(ans,min(f[n][i][0],f[n][i][1]));
printf("%.2lf\n",ans);
return 0;
}