之前一直觉得网络流是一个非常高端很难学的东西,感觉以自己的水平可能一辈子都不会去学这么难的东西了,感谢BUPT这次多校中把最大流作为签到题,强迫我去学了一波,看了很久,过程也非常艰难,最终还是把这道题给补出来了。看到自己的进步还是很开心的,也许这就是玩算法竞赛的意义吧.
这一道题目的目的是要求通过删掉一些边,使得图中1到n的最短路边长,求删除边的最小权值和。简单分析一下,1到n可能有多条最短路,不在这些最短路上的边对最短路没有贡献,对题目无用,所以直接忽略。于是题目转化成求1到n所有最短路组成的新的一幅图的最小割,建立新图时用dijkstra算出所有点到1的最短路dis[i],对满足dis[x]+dis[y]+边权值(x->y)的x和y,这两点一定在新的图中。学了网络流之后知道,最小割>=最大流,在图为残余网络的时候等号成立。这道题目就变成求两点间的最短路集合,然后上最大流的板子(https://blog.nowcoder.net/n/9862adc1f52c4c78aa54f62361eb90b1)。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e4 + 3; const ll INF = 0x3f3f3f3f3f3f3f3f; struct Edge1 { int to,Next; ll w; }edge[maxn<<1]; int n,tot,num,head[maxn],layer[maxn],ver[maxn],nxt[maxn],hd[maxn],vis[maxn]; ll dis[maxn],e[maxn]; ll max(ll a,ll b){return a>b?a:b;}; ll min(ll a,ll b){return a<b?a:b;}; void ADD(int x,int y,ll z) { ver[++num]=y,e[num]=z,nxt[num]=hd[x],hd[x]=num; } void add(int x,int y,ll z) { edge[++tot].to=y,edge[tot].w=z,edge[tot].Next=head[x],head[x]=tot; edge[++tot].to=x,edge[tot].w=0,edge[tot].Next=head[y],head[y]=tot; } void Dijkstra(int s) { priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > PQ; PQ.push(make_pair(0,s)); for(int i=1;i<=n;i++) vis[i]=0,dis[i]=INF; dis[s]=0; while(!PQ.empty()) { ll z; int x=PQ.top().second,y; PQ.pop(); if(vis[x]) continue; vis[x]=1; for(int i=hd[x];i;i=nxt[i]) { y=ver[i],z=e[i]; if(dis[y]>dis[x]+z) { dis[y]=dis[x]+z; PQ.push(make_pair(dis[y],y)); } } } } bool bfs(int s,int t) { for(int i=1;i<=n;i++) layer[i]=-1; layer[s]=0; queue<int> q; q.push(s); while(!q.empty()) { int x=q.front(),y; q.pop(); for(int i=head[x];i;i=edge[i].Next) { y=edge[i].to; if(edge[i].w&&layer[y]==-1) { layer[y]=layer[x]+1; q.push(y); if(y==t) return true; } } } return false; } ll Dinic(int x,int t,ll flow) { if(x==t) return flow; ll rest=flow,k; int y; for(int i=head[x];i&&rest;i=edge[i].Next) { y=edge[i].to; if(edge[i].w&&layer[y]==layer[x]+1) { k=Dinic(y,t,min(rest,edge[i].w)); if(!k) layer[y]=-1; edge[i].w-=k; edge[i^1].w+=k; rest-=k; } } return flow-rest; } int main() { int t; scanf("%d",&t); while(t--) { int m,x,y; ll z; scanf("%d%d",&n,&m); tot=1,num=0; for(int i=1;i<=n;i++) head[i]=0; for(int i=1;i<=m;i++) { scanf("%d%d%lld",&x,&y,&z); ADD(x,y,z); } Dijkstra(1); if(dis[n]==INF) { printf("0\n"); continue; } for(int i=1;i<=n;i++) { x=i; for(int j=hd[x];j;j=nxt[j]) { y=ver[j]; if(dis[x]+e[j]==dis[y]) add(x,y,e[j]); } } ll flow=0,maxflow=0; while(bfs(1,n)) while(flow=Dinic(1,n,INF)) maxflow+=flow; printf("%lld\n",maxflow); } return 0; }