传送门
NOIP 2016 D1T3
题意:有n个时间段,第i个时间段可以选择在ci教室上课,也可以选择申请换课,有ki概率申请通过,在di上课,另外1−ki的概率留在ci教室。
总共有v个教室,e条路径双向联通教室xi和yi,路径有权值wi。在课间时(相邻两个时间段的间隔中),你要从上一个教室走最短路径到下一个教室。
现在你有m次申请机会,只能提前申请一堆换课(也就是你不能在知道某一次申请结果后再去申请下一个换课)。求总距离的最小期望。
1≤n≤2000,0≤m≤2000,1≤v≤300,0≤e≤90000,1≤wi≤100,0≤ki≤1
题解:设f[i][j][k]表示前i个时间段,选了j个课程,当前k=0/1表示选/不选的最短路径期望。
然后分四种情况:(0,0),(0,1),(1,0),(1,1)讨论,为什么要讨论前一种情况呢,因为上一次换不换会影响到这次。
P.S.Floyd这种东西打慢点,打快了写错一个字母就是40分钟( ̄工 ̄lll)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int c[2002],d[2002],n,m,v,e;
double k[2002],dis[302][302],ans=0,f[2002][2002][2];
int main() {
freopen("P1850.in","r",stdin);
scanf("%d%d%d%d",&n,&m,&v,&e);
for (int i=1;i<=n;++i) scanf("%d",&c[i]);//original position
for (int i=1;i<=n;++i) scanf("%d",&d[i]);//new position
for (int i=1;i<=n;++i) scanf("%lf",&k[i]);//probability
memset(dis,127,sizeof(dis));
for (int i=1;i<=e;++i) {
int u,v;double w;
scanf("%d%d%lf",&u,&v,&w);
dis[u][v]=min(dis[u][v],w);
dis[v][u]=min(dis[v][u],w);
}
for (int i=1;i<=v;++i) dis[i][i]=0;
for (int s=1;s<=v;++s)//Floyd
for (int i=1;i<=v;++i)
for (int j=1;j<=v;++j)
dis[i][j]=min(dis[i][j],dis[i][s]+dis[s][j]);
memset(f,127,sizeof(f));
f[1][0][0]=f[1][1][1]=0;
for (int i=2;i<=n;++i) {
int cur=min(i,m);
for (int j=0;j<=cur;++j) {
f[i][j][0]=f[i-1][j][0]+dis[c[i-1]][c[i]];
f[i][j][0]=min(f[i][j][0],f[i-1][j][1]+dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]));
if (j) {
f[i][j][1]=f[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1-k[i]);
f[i][j][1]=min(f[i][j][1],f[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i]);
}
}
}//expectation
ans=1e9+7;
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;
}