1. 程式人生 > >BZOJ2324 ZJOI2011營救皮卡丘(floyd+上下界費用流)

BZOJ2324 ZJOI2011營救皮卡丘(floyd+上下界費用流)

cstring span 網絡流 mes nbsp bzoj getc set code

  雖然不一定每次都是由編號小的點向編號大的走,但一個人摧毀的順序一定是從編號小的到編號大的。那麽在摧毀據點x的過程中,其只能經過編號小於x的點。並且這樣一定合法,因為可以控制其他人先去摧毀所經過的點。那麽可以floyd求出由摧毀x到摧毀y的最短路徑。註意這裏也需要更新起點編號大於終點的情況,否則floyd會掛掉。

  剩下的問題就是用k條路徑覆蓋所有點使費用最小。那麽考慮網絡流。可以將每個點拆成入點和出點來控制節點流量至少為1,邊權的費用設置為其間最短路徑,跑上下界費用流即可。

#include<iostream> 
#include<cstdio>
#include
<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f; } #define N 310 #define M 20010 #define S 302 #define T 303 #define in(x) (x<<1) #define out(x) (x<<1|1) int n,m,k,p[N],dis[N][N],t=-1,ans=0; int d[N],q[N],pre[N]; bool flag[N]; struct data{int to,nxt,cap,flow,cost; }edge[M<<3]; void addedge(int x,int y,int z,int c) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0
,edge[t].cost=c,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,edge[t].cost=-c,p[y]=t; } int inc(int &x){x++;if (x>T+2) x-=T+2;return x;} bool spfa() { memset(d,42,sizeof(d));d[S]=0; memset(flag,0,sizeof(flag)); int head=0,tail=1;q[1]=S; do { int x=q[inc(head)];flag[x]=0; for (int i=p[x];~i;i=edge[i].nxt) if (d[x]+edge[i].cost<d[edge[i].to]&&edge[i].flow<edge[i].cap) { d[edge[i].to]=d[x]+edge[i].cost; pre[edge[i].to]=i; if (!flag[edge[i].to]) q[inc(tail)]=edge[i].to,flag[edge[i].to]=1; } }while (head!=tail); return d[T]<=700000000; } void ekspfa() { while (spfa()) { int v=k; for (int i=T;i!=S;i=edge[pre[i]^1].to) v=min(v,edge[pre[i]].cap-edge[pre[i]].flow); for (int i=T;i!=S;i=edge[pre[i]^1].to) ans+=v*edge[pre[i]].cost,edge[pre[i]].flow+=v,edge[pre[i]^1].flow-=v; } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2324.in","r",stdin); freopen("bzoj2324.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),k=read(); memset(p,255,sizeof(p)); memset(dis,42,sizeof(dis)); for (int i=0;i<=n;i++) dis[i][i]=0; for (int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); dis[x][y]=dis[y][x]=min(dis[x][y],z); } for (int k=0;k<=n;k++) for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) if (i>=k||j>=k) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); for (int i=0;i<n;i++) for (int j=i+1;j<=n;j++) addedge(out(i),in(j),k,dis[i][j]); for (int i=0;i<=n;i++) addedge(in(i),out(i),k,0); for (int i=0;i<=n;i++) addedge(in(i),T,1,0),addedge(S,out(i),1,0),addedge(out(i),out(n),k,0); addedge(out(n),in(0),k,0); ekspfa(); cout<<ans; return 0; }

BZOJ2324 ZJOI2011營救皮卡丘(floyd+上下界費用流)