hdu6214 Smallest Minimum Cut(边数最少的最小割,网络流建图)

tech2023-01-20  114

题意:

给定带有源点S和汇点T的n个点m条边的带权有向图, 该图有若干S-T最小割,要求找到割的边数最少的最小割,输出这个最少边数。

数据范围:n<=200,m<=1000,边权<=255

解法:

假设图中共m条边, 将图中的每条边边权x变为x*(m+1)+1, 然后计算最小割maxflow,最小割的最少边数为maxflow%(m+1). 我自己理解的原理(不保证正确): 如果将每条边的边权乘上一个数p, 显然现在最小割也为原来的p倍, 如果将每条边的边权乘上一个数p之后再加1, 那么现在最小割就是原来的p倍加上边数. 最小割多出来的边数就是最少边数. (巧妙的利用了权值的影响,妙啊) 为什么要乘上p呢?因为要避免乘上p之后影响整个图的最小割. 一般取p=m+1,刚好为图的边数+1, 因为图在形成一条链的时候,一共是+m,要取的比m大才不影响, 取太大乘起来可能会爆,所以一般取m+1最合适.

code:

#include<bits/stdc++.h> using namespace std; #define ll long long const int maxm=1e6+5; struct Dinic{ static const int DN=1e4+5; static const int DM=1e5+5; static const ll inf=1e16; int head[DN],nt[DM],to[DM],tot; ll w[DM]; int d[maxm]; ll maxflow; int st,ed; int n; bool bfs(){ queue<int>q; q.push(st); for(int i=1;i<=n;i++)d[i]=0; d[st]=1; while(!q.empty()){ int x=q.front();q.pop(); for(int i=head[x];i;i=nt[i]){ int v=to[i]; if(w[i]&&!d[v]){ d[v]=d[x]+1; q.push(v); if(v==ed)return 1; } } } return 0; } ll dfs(int x,ll flow){ if(x==ed)return flow; ll res=flow; for(int i=head[x];i;i=nt[i]){ int v=to[i]; if(w[i]&&d[v]==d[x]+1){ int k=dfs(v,min(res,w[i])); w[i]-=k; w[i^1]+=k; res-=k; if(!k)d[v]=-1; if(!res)break; } } return flow-res; } ll dinic(){ while(bfs()){ maxflow+=dfs(st,inf); } return maxflow; } // void init(){ for(int i=0;i<=n;i++)head[i]=0; tot=1; // maxflow=0; } void add(int x,int y,ll z){ tot++;nt[tot]=head[x];head[x]=tot;to[tot]=y;w[tot]=z; } void add2(int x,int y,ll z){ add(x,y,z); add(y,x,0); } }D; int n,m; signed main(){ int T;cin>>T; while(T--){ scanf("%d%d",&n,&m); D.n=n; D.init(); scanf("%d%d",&D.st,&D.ed); for(int i=1;i<=m;i++){ int a,b,c;scanf("%d%d%d",&a,&b,&c); c=c*(m+1)+1; D.add2(a,b,c); } ll ans=D.dinic()%(m+1); printf("%lld\n",ans); } return 0; }
最新回复(0)