BZOJ4720 [Noip2016]换教室
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
Input
Output
Sample Input
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1
Sample Output
24分
注意到有6个测试点m=0,则说明不能提出申请,那么只需要求出全图的两两之间的最短路,路径唯一确定。
52分
注意到另外有7个测试点m=1,只能提出一次申请。我们可以直接枚举在哪里提出申请,再与不申请的时候取一个min就可以了。
76分
注意到还有6个测试点m=2,只能提出两次申请,暴力枚举哪两个点申请即可。
80分
其实我们并不需要分那么多类情况讨论,考虑直接爆搜,在每个点是否提出申请,最后暴力计算贡献。因为这样的复杂度是C(n,m)的,所以m<=2和n<=10是完全没有问题的,直接可以用搜索拿到80分。
100分
这显然是一道概率DP裸题。考虑f[i][j][0、1]表示前i堂课,已经申请了j次,这次申不申请的最小期望值,则:
$${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-p[i-1])+dis(d[i-1],c[i])*p[i] )}$$
$${f[i][j][1]=min( f[i-1][j-1][0]+dis(c[i-1],d[i])*p[i]+dis(c[i-1],c[i])*(1-p[i]),f[i-1][j-1][1]+dis(c[i-1],c[i])*(1-p[i-1])*(1-p[i])+dis(c[i-1],d[i])*(1-p[i-1])*p[i]+dis(d[i-1],c[i])*p[i-1]*(1-p[i]))+dis(d[i-1],d[i])*p[i-1]*p[i]) }$$
(dis(i,j)表示i到j的最短距离)
需要说明的是,期望是可以线性相加的,也就是说一条路径上的距离总期望值实际上就等于每两个相邻点之间的距离的期望值的总和。另外上面的转移式可以这样理解:在我们已经提出申请的情况下,对于所有情况的讨论就是在现有的申请情况下的能得到的距离期望值,事实上是根据计算了每条边的贡献。
这样即可获得满分。时间复杂度:O(V3+nm)
注意事项
如果状态的0、1表示的是地点在c还是d的话会有不少问题,因为压根就不能体现出申请的时候的成功与失败结果,但是对于m<=2的点基本不会出错,大点的出错概率也不是特别大,就导致这样完全错误的状态设计在联赛数据下获得了88分……
1 //It is made by ljh2000 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #include <string> 14 using namespace std; 15 typedef long long LL; 16 const int MAXN = 2011; 17 const int MAXD = 311; 18 const int inf = (1<<29); 19 int n,m,D,bian,c[MAXN],d[MAXN],dis[MAXD][MAXD]; 20 double f[MAXN][MAXN][2],k[MAXN],ans; 21 22 inline int getint(){ 23 int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); 24 if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; 25 } 26 27 inline void work(){ 28 n=getint(); m=getint(); D=getint(); bian=getint(); int x,y,z,lim; double minl; 29 for(int i=1;i<=n;i++) c[i]=getint(); for(int i=1;i<=n;i++) d[i]=getint(); for(int i=1;i<=n;i++) scanf("%lf",&k[i]); 30 for(int i=1;i<=D;i++) for(int j=1;j<=D;j++) dis[i][j]=inf; 31 for(int i=1;i<=bian;i++) { 32 x=getint(); y=getint(); z=getint(); if(dis[x][y]==inf) dis[x][y]=dis[y][x]=z; 33 else dis[x][y]=min(dis[x][y],z),dis[y][x]=dis[x][y]; 34 } 35 for(int l=1;l<=D;l++) for(int i=1;i<=D;i++) if(i!=l) for(int j=1;j<=D;j++) if(j!=l&&i!=j) dis[i][j]=min(dis[i][l]+dis[l][j],dis[i][j]); 36 for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=1e30; f[1][0][0]=f[1][1][1]=0; 37 for(int i=1;i<=D;i++) dis[i][i]=0; 38 for(int i=2;i<=n;i++) { 39 lim=min(i,m); 40 for(int j=0;j<=lim;j++) { 41 minl=f[i-1][j][1]+dis[c[i-1]][c[i]]*(1.0-k[i-1])+dis[d[i-1]][c[i]]*k[i-1]; 42 minl=min(minl,f[i-1][j][0]+dis[c[i-1]][c[i]]); 43 f[i][j][0]=min(f[i][j][0],minl); 44 if(j>=1) { 45 minl=f[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1.0-k[i])*(1.0-k[i-1])+dis[c[i-1]][d[i]]*(1.0-k[i-1])*k[i]; 46 minl+=dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i]; 47 minl=min(minl,f[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1.0-k[i])); 48 f[i][j][1]=min(f[i][j][1],minl); 49 } 50 } 51 } 52 ans=1e30; for(int i=0;i<=m;i++) ans=min(ans,min(f[n][i][0],f[n][i][1])); 53 printf("%.2lf",ans); 54 } 55 56 int main() 57 { 58 work(); 59 return 0; 60 }