题目大意
有
n
个时间段,第
总共有
v
个教室,
现在你有
m
次申请机会,只能提前申请一堆换课(也就是你不能在知道某一次申请结果后再去申请下一个换课)。求总距离的最小期望。
题目分析
这题是很水的期望题。
由期望的线性性
E[X]=∑i=2nE[dis(i−1,i)]
因此我们可以分开来计算每一个课间的期望距离。
令 fi,j,0/1 表示当前在第 i 个时间段,已经申请了
然后直接枚举上一个课间是否申请,分类讨论各自的概率(申请包括了是否通过)转移就好了。这个很简单,相信大家都懂。
至于最短路,直接上 Floyd 。
时间复杂度 O(v3+nm) 。
代码实现
#include <iostream>
#include <climits>
#include <cfloat>
#include <cstdio>
#include <cctype>
using namespace std;
typedef double db;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int INF=INT_MAX/3;
const db FINF=DBL_MAX/3;
const int N=2005;
const int M=2005;
const int V=305;
const int E=90005;
db f[N][M][2];
int dis[V][V];
int c[N],d[N];
int n,m,v,e;
db p[N];
db ans;
void floyd()
{
for (int k=1;k<=v;k++)
for (int i=1;i<=v;i++)
if (i!=k)
for (int j=1;j<=v;j++)
if (i!=j&&j!=k)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
void dp()
{
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
f[i][j][0]=f[i][j][1]=FINF;
f[1][0][0]=f[1][1][1]=0;
for (int i=2;i<=n;i++)
for (int j=0;j<=i&&j<=m;j++)
{
int dxu=dis[c[i-1]][c[i]],dxv=dis[c[i-1]][d[i]],dyu=dis[d[i-1]][c[i]],dyv=dis[d[i-1]][d[i]];
db p1=p[i-1],p2=p[i];
if (j<=i-1) f[i][j][0]=min(p1*dyu+(1-p1)*dxu+f[i-1][j][1],dxu+f[i-1][j][0]);
if (j) f[i][j][1]=min(f[i-1][j-1][0]+p2*dxv+(1-p2)*dxu,f[i-1][j-1][1]+p1*p2*dyv+p1*(1-p2)*dyu+(1-p1)*p2*dxv+(1-p1)*(1-p2)*dxu);
}
ans=FINF;
for (int j=0;j<=m&&j<=n;j++) ans=min(ans,min(f[n][j][0],f[n][j][1]));
}
int main()
{
freopen("classroom.in","r",stdin),freopen("classroom.out","w",stdout);
n=read(),m=read(),v=read(),e=read();
for (int i=1;i<=n;i++) c[i]=read();
for (int i=1;i<=n;i++) d[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++)
dis[i][j]=INF;
dis[i][i]=0;
}
for (int i=1,x,y,z;i<=e;i++) x=read(),y=read(),z=read(),dis[x][y]=min(dis[x][y],z),dis[y][x]=min(dis[y][x],z);
floyd(),dp();
printf("%.2lf\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}