题意:
给定带有源点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;
}