3 4 2 0 1 3 0 2 4 1 3 2 2 3 2 0 0 0
14
题意:给出一个图,n+1个点(包括0),m条边,有k个警察,遍历点的时候如果到i点抓了犯人,i-1点的犯人就会逃跑,任务就失败,现在让你求抓到所有犯人需要的最小费用。
思路:由于题目数据给的比较小,最多100个点,用floyd处理出来每两个点的最短路,然后拆点,建立源点和汇点,源点连接0,路程k(由于最多k个人),费用0。然后0连接汇点,路程k,费用0。然后就是中间的连边处理,从0出发的,所以连接0到n个点,路程为1,费用为0到i点的最短路。接着拆点,为了经过每个点,连接i和i+n,路程1,费用-100000。接着连接i+n到j(i<j,题意所迫)路程1,费用为i到j的最短路。最后,i+n到汇点建边,路程1,费用为i到0的最短路。跑完费用流之后用结果加n*100000,因为把两两个拆的点之间的费用设为了最小,肯定会经过这n个-100000,所以结果再加上来就ok!
这里贴上费用流模板:
const int oo=1e9; const int mm=11111111; const int mn=888888; int node,src,dest,edge; int ver[mm],flow[mm],cost[mm],nex[mm]; int head[mn],dis[mn],p[mn],q[mn],vis[mn]; /**这些变量基本与最大流相同,增加了 cost 表示边的费用, p 记录可行流上节点对应的反向边 */ void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0; i<node; i++)head[i]=-1,vis[i]=0; edge=0; } void addedge(int u,int v,int f,int c) { ver[edge]=v,flow[edge]=f,cost[edge]=c,nex[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,cost[edge]=-c,nex[edge]=head[v],head[v]=edge++; } /**以上同最大流*/ /**spfa 求最短路,并用 p 记录最短路上的边*/ bool spfa() { int i,u,v,l,r=0,tmp; for(i=0; i<node; ++i)dis[i]=oo; dis[q[r++]=src]=0; p[src]=p[dest]=-1; for(l=0; l!=r; (++l>=mn)?l=0:l) for(i=head[u=q[l]],vis[u]=0; i>=0; i=nex[i]) if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i])) { dis[v]=tmp; p[v]=i^1; if(vis[v]) continue; vis[q[r++]=v]=1; if(r>=mn)r=0; } return p[dest]>-1; } /**源点到汇点的一条最短路即可行流,不断的找这样的可行流*/ int SpfaFlow() { int i,ret=0,delta; while(spfa()) { for(i=p[dest],delta=oo; i>=0; i=p[ver[i]]) if(flow[i^1]<delta)delta=flow[i^1]; for(i=p[dest]; i>=0; i=p[ver[i]]) flow[i]+=delta,flow[i^1]-=delta; ret+=delta*dis[dest]; } return ret; }
ac代码:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<iomanip> using namespace std; typedef long long ll; const int oo=1e9; const int mm=11111111; const int mn=888888; int node,src,dest,edge; int ver[mm],flow[mm],cost[mm],nex[mm]; int head[mn],dis[mn],p[mn],q[mn],vis[mn]; /**这些变量基本与最大流相同,增加了 cost 表示边的费用, p 记录可行流上节点对应的反向边 */ void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0; i<node; i++)head[i]=-1,vis[i]=0; edge=0; } void addedge(int u,int v,int f,int c) { ver[edge]=v,flow[edge]=f,cost[edge]=c,nex[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,cost[edge]=-c,nex[edge]=head[v],head[v]=edge++; } /**以上同最大流*/ /**spfa 求最短路,并用 p 记录最短路上的边*/ bool spfa() { int i,u,v,l,r=0,tmp; for(i=0; i<node; ++i)dis[i]=oo; dis[q[r++]=src]=0; p[src]=p[dest]=-1; for(l=0; l!=r; (++l>=mn)?l=0:l) for(i=head[u=q[l]],vis[u]=0; i>=0; i=nex[i]) if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i])) { dis[v]=tmp; p[v]=i^1; if(vis[v]) continue; vis[q[r++]=v]=1; if(r>=mn)r=0; } return p[dest]>-1; } /**源点到汇点的一条最短路即可行流,不断的找这样的可行流*/ int SpfaFlow() { int i,ret=0,delta; while(spfa()) { for(i=p[dest],delta=oo; i>=0; i=p[ver[i]]) if(flow[i^1]<delta)delta=flow[i^1]; for(i=p[dest]; i>=0; i=p[ver[i]]) flow[i]+=delta,flow[i^1]-=delta; ret+=delta*dis[dest]; } return ret; } int tu[110][110]; void floyd(int n) { for(int k=0; k<=n; k++) for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) tu[i][j]=min(tu[i][j],tu[i][k]+tu[k][j]); } int main() { int n,m,k; while(~scanf("%d%d%d",&n,&m,&k)&&n+m+k) { int st=2*n+1,ed=2*n+2; prepare(2*n+3,st,ed); for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) tu[i][j]=i==j?0:oo; while(m--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(c<tu[a][b]) tu[a][b]=tu[b][a]=c; } floyd(n); addedge(st,0,k,0); addedge(0,ed,k,0); for(int i=1; i<=n; i++) addedge(0,i,1,tu[0][i]),addedge(i,i+n,1,-100000),addedge(i+n,ed,1,tu[i][0]); for(int i=1; i<=n; i++) for(int j=i+1; j<=n; j++) addedge(i+n,j,1,tu[i][j]); printf("%d\n",SpfaFlow()+100000*n); } return 0; }