终于ac了,,好开心啊,,
这道题我第一眼看到时头很大,因为题干很长,不过类型很好判断
概率dp+最短路
首先floyed求出两两间的最短路,记为d[i][j]
f[i][j][1/0]表示在第i个时间段,申请了j个教室,1表示通过,
0表示不通过
a[i]表示原来时间段的教室,b[i]表示更换后的教室,c[i]表示申请成功的概率
转移状态时,当前时间段是由上一个时间段申请转移和不申请转移
申请又分为申请通过和不通过
于是就有了状态转移方程:
f[i][j][0] = min(f[i-1][j][0] + d[a[i-1]][a[i]], f[i-1][j][1] + d[a[i-1]][a[i]] (1 - c[i-1]) + d[b[i-1]][a[i]] c[i-1]);
f[i][j][1] = min(f[i-1][j-1][0] + d[a[i-1]][a[i]] (1.0-c[i]) + d[a[i-1]][b[i]] c[i], f[i-1][j-1][1] + d[a[i-1]][a[i]] (1.0-c[i-1]) (1.0-c[i]) + d[b[i-1]][a[i]] c[i-1] (1.0-c[i]) + d[a[i-1]][b[i]] (1.0-c[i-1]) c[i] + d[b[i-1]][b[i]] c[i-1] c[i]);
附上代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2018,maxm=310;
int a[maxn],b[maxn];
double d[maxm][maxm],c[maxn],f[maxn][maxn][2];
int cnt=0;
int n,m,v,e;
void init()
{
for(int i=0;i<maxm;i++)
for(int j=0;j<maxm;j++)
if(i==j)d[i][j]=0;
else d[i][j]=1e9;
for (int i=0;i<maxn;i++)
for (int j=0;j<maxn;j++)
f[i][j][0]=f[i][j][1]=1e9;
f[1][0][0]=0;f[1][1][1]=0;
}
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",&c[i]);
init();
for(int i=1;i<=e;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(x==y)continue;
if(z<d[x][y])d[x][y]=z;
d[y][x]=d[x][y];
}
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
if(d[i][k]+d[k][j]<d[i][j])
d[i][j]=d[i][k]+d[k][j];
for(int i=2;i<=n;i++)
{
f[i][0][0]=f[i-1][0][0]+d[a[i-1]][a[i]];
for(int j=1;j<=min(m,i);j++)
{
f[i][j][0]=min(f[i-1][j][0]+d[a[i-1]][a[i]],f[i-1][j][1]+d[a[i-1]][a[i]]*(1.0-c[i-1])+d[b[i-1]][a[i]]*c[i-1]);
f[i][j][1]=f[i-1][j-1][0]+d[a[i-1]][a[i]]*(1.0-c[i])+d[a[i-1]][b[i]]*c[i];
double t=f[i-1][j-1][1]+d[a[i-1]][a[i]]*(1.0-c[i-1])*(1.0-c[i]);
t+=d[b[i-1]][a[i]]*c[i-1]*(1.0-c[i]);
t+=d[a[i-1]][b[i]]*(1.0-c[i-1])*c[i];
t+=d[b[i-1]][b[i]]*c[i-1]*c[i];
f[i][j][1]=min(f[i][j][1],t);
}
}
double ans=f[n][0][0];
for(int i=1;i<=m;i++)
{
ans=min(ans,min(f[n][i][0],f[n][i][1]));
}
printf("%.2lf",ans);
return 0;
}