题目大意:现有v个点和e条边。在n个时刻中,某个时刻i应该前往c[i],可以申请前往d[i],有p[i]的几率成功,每个时刻只能申请一次,最多申请m次。问如何分配申请使得期望经过的边权最小。
v<=300,n,m<=2000
设状态为f(i,j,k)表示在第i个时刻已经申请了j次,k表示当前时刻是否申请。
分别按照这一时刻申请是否成功和上一时刻申请是否成功讨论。
具体转移见代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2005
#define M 305
#define INF 100000000
using namespace std;
namespace Graph {
int n,m,d[M][M];
void read() {
memset(d,0x3f,sizeof d);
for(int i=1;i<=n;++i) d[i][i]=0;
for(int i=1,x,y,z;i<=m;++i)
scanf("%d%d%d",&x,&y,&z),
d[x][y]=d[y][x]=min(d[x][y],z);
return ;
}
void init() {
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
return ;
}
}
namespace DP {
int n,m,a[N],b[N];
double f[N][N][2],p[N];
void read() {
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",p+i);
return ;
}
double solve() {
using Graph::d;
for(int i=1;i<=n;++i)
for(int j=0;j<=m;++j)
f[i][j][0]=f[i][j][1]=INF;
f[1][0][0]=f[1][1][1]=0;
for(int i=2;i<=n;++i) {
for(int j=0;j<=m;++j) {
double minn=INF,tmp;
tmp=f[i-1][j][0];
tmp+=d[a[i-1]][a[i]];
minn=min(minn,tmp);
tmp=f[i-1][j][1];
tmp+=d[a[i-1]][a[i]]*(1-p[i-1]);
tmp+=d[b[i-1]][a[i]]*p[i-1];
minn=min(minn,tmp);
f[i][j][0]=minn;
if(!j) continue;
minn=INF;
tmp=f[i-1][j-1][0];
tmp+=d[a[i-1]][b[i]]*p[i];
tmp+=d[a[i-1]][a[i]]*(1-p[i]);
minn=min(minn,tmp);
tmp=f[i-1][j-1][1];
tmp+=d[a[i-1]][a[i]]*(1-p[i-1])*(1-p[i]);
tmp+=d[a[i-1]][b[i]]*(1-p[i-1])*p[i];
tmp+=d[b[i-1]][a[i]]*p[i-1]*(1-p[i]);
tmp+=d[b[i-1]][b[i]]*p[i-1]*p[i];
minn=min(minn,tmp);
f[i][j][1]=minn;
}
}
double ans=INF;
for(int i=0;i<=m;++i) ans=min(ans,min(f[n][i][0],f[n][i][1]));
return ans;
}
}
int main() {
scanf("%d%d%d%d",&DP::n,&DP::m,&Graph::n,&Graph::m);
DP::read(), Graph::read();
Graph::init();
printf("%.2f\n",DP::solve());
return 0;
}