【传送门:BZOJ1266】
简要题意:
给出一个有n个点,m条无向边的图,每条边有长度和摧毁的代价,首先求出从1到n的最短路径,然后要求花费最小代价摧毁一些边,使得图中1到n的最短路径变长,求出最小代价
题解:
求最短路径,简直。。好吧,SPFA或floyd搞定
求最小代价,我们可以想一下,先把所有最短路径找出来,然后删除一些边,使得所有路径都有边被摧毁,有可能有多条路径覆盖了同一条边
仔细想了想,嗯,用最小代价使所有路径都有边,嗯,用最小代价使1到n不连通!!这不是最小割吗!!
好的,因为我们要将所有路径都求出来并建网络流的图,所以我们用floyd来求最短路径(因为这样比较方便)
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int f[510][510]; int x[210000],y[210000],d[210000],t[210000]; struct node { int x,y,c,next,other; }a[410000];int len,last[510]; void ins(int x,int y,int c) { int k1=++len,k2=++len; a[k1].x=x;a[k1].y=y;a[k1].c=c; a[k1].next=last[x];last[x]=k1; a[k2].x=y;a[k2].y=x;a[k2].c=0; a[k2].next=last[y];last[y]=k2; a[k1].other=k2; a[k2].other=k1; } int h[510],list[510]; int st,ed; bool bt_h() { memset(h,0,sizeof(h));h[st]=1; list[1]=st; int head=1,tail=1; while(head<=tail) { int x=list[head]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(a[k].c>0&&h[y]==0) { h[y]=h[x]+1; list[++tail]=y; } } head++; } if(h[ed]==0) return false; else return true; } int findflow(int x,int f) { if(x==ed) return f; int s=0,t; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(h[y]==h[x]+1&&a[k].c>0&&f>s) { t=findflow(y,min(a[k].c,f-s)); s+=t; a[k].c-=t; a[a[k].other].c+=t; } } if(s==0) h[x]=0; return s; } int main() { int n,m; scanf("%d%d",&n,&m); len=0;memset(last,0,sizeof(last)); memset(f,63,sizeof(f)); for(int i=1;i<=n;i++) f[i][i]=0; for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&x[i],&y[i],&d[i],&t[i]); f[x[i]][y[i]]=d[i]; f[y[i]][x[i]]=d[i]; } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) if(i!=k) for(int j=1;j<=n;j++) if(j!=i&&j!=k) f[i][j]=min(f[i][j],f[i][k]+f[k][j]); int dd=f[1][n]; st=1;ed=n; for(int i=1;i<=m;i++) { if(f[1][x[i]]+d[i]+f[y[i]][n]==dd) ins(x[i],y[i],t[i]); if(f[x[i]][n]+d[i]+f[1][y[i]]==dd) ins(y[i],x[i],t[i]); } int ans=0; while(bt_h()==true) { ans+=findflow(st,999999999); } printf("%d\n%d\n",f[1][n],ans); return 0; }