【题解】
"使得总成本尽可能地小",属于最优化问题; m表示码头总数,1<=m<=20,想到用集合表状态(二进制)
整个过程按日期顺序进行,因此设:f[i]:前i天的最小花费状态转移:
若在第j天最后一次更改线路(1<=j<=i),则扣除k(修改成本)后,第j~i天的总花费就是:A到B的最短路*(i-j+1),
但由于第j~i天会有航线不让走,所以从第j天开始,选择的最短路不能经过第j~i天期间禁行过的航线
既然j不同,"地图"就不同,且这个"地图"总共只有2^m种情况,我们可以针对每种情况,求出最短路,以后直接调用(预处理)
j==0时不用加k(第一天制定航线不用交k元)
这道题在BZOJ第一页里,应该算是简单的了吧,然而我为什么不觉得。。。
#include<stdio.h> #include<stdlib.h> int d[300000][25]={0},G[25][25]={0},s[105]={0},f[105]={0},q[1005]={0},hash[25]={0}; int main() { int n,m,k,e,i,j,zt,x,y,z,head,tail; scanf("%d%d%d%d",&n,&m,&k,&e); for(i=1;i<=m;i++) for(j=1;j<=m;j++) if(i!=j) G[i][j]=-1; for(i=1;i<=e;i++) { scanf("%d%d%d",&x,&y,&z); if(G[x][y]==-1||G[x][y]>z) G[x][y]=G[y][x]=z; } for(i=1;i<=n;i++) s[i]=(1<<m-2)-1; scanf("%d",&j); for(;j>0;j--) { scanf("%d%d%d",&x,&y,&z); for(i=y;i<=z;i++) if((s[i]&(1<<x-2))>0) s[i]-=(1<<x-2);//表示出第i天允许码头集s[i] } for(i=0;i<=(1<<m-2)-1;i++)//预处理,SPFA算法 { for(j=2;j<=m;j++) { d[i][j]=-1; hash[j]=0; } head=0; tail=1; q[0]=1; hash[1]=1; while(head<tail) { for(j=2;j<=m;j++) if( (j==m || (i&(1<<j-2))>0) && G[q[head]][j]>=0 && (d[i][j]==-1 || d[i][j]>d[i][q[head]]+G[q[head]][j]) ) {//(i&(1<<j-2))>0 不能写成 i&(1<<j-2)==1 d[i][j]=d[i][q[head]]+G[q[head]][j]; if(hash[j]==0) { q[tail++]=j; hash[j]=1; } } hash[q[head]]=0; head++; } } for(i=1;i<=n;i++) { zt=s[i]; f[i]=1000000000; for(j=i;j>=1;j--)//zt:状态--受第j~i天影响的 第j天的路线通断情况 { zt&=s[j]; if(d[zt][m]==-1) break;//前推到第j天,A,B不通 if(j!=1) { if(f[i]>f[j-1]+k+d[zt][m]*(i-j+1)) f[i]=f[j-1]+k+d[zt][m]*(i-j+1); } else if(f[i]>f[j-1]+d[zt][m]*(i-j+1)) f[i]=f[j-1]+d[zt][m]*(i-j+1); } } printf("%d",f[n]); return 0; }