1. 程式人生 > >最小割最小割邊

最小割最小割邊

#include <cstdio>//最小割,圖的割邊,割斷後無法從源點到匯點,最小割邊的容量和為該圖的最大流;
#include <cstring>//最大點權獨立和=所有點的權值和-最小割
#include <algorithm>//點即是邊,拆點,拆邊
#include <iostream>
#include <queue>
using namespace std;
#define LL long long
const LL INF=0x3f3f3f3f;
const LL mod=200010;
LL f[1000010],n,m,cont,arr[1010][1010
],dis[1000010],gap[1000010],first[1000010]; LL cur[1000010];//當前弧 LL exc[1000010];//該點盈餘 LL pre[1000010];//前驅節點 struct Node { LL u,v,w; LL next; }x[1000010]; void add_edge(LL a,LL b,LL value) { x[cont].u=a,x[cont].v=b,x[cont].w=value; x[cont].next=first[a],first[a]=cont++; x[cont].u=b,x[cont].v=a,x[cont].w=0; x[cont].next=first[b],first[b]=cont++; } LL SAP(LL s,LL e,LL N)//源點,匯點,總點數
{ LL Max_flow=0,u=s; pre[s]=-1; exc[s]=INF; memset(gap,0,sizeof(gap));//標號是i的點的個數 memset(dis,0,sizeof(dis));//距離標號 gap[0]=N; for(LL i=0; i<=N; i++) //初始化當前弧 cur[i]=first[i]; while(dis[s]<N) { LL flag=1; if(u==e)//找到匯點 { Max_flow+=exc[e];//更新最大流
for(LL i=pre[e]; i!=-1; i=pre[i]) //更新當前允許路 { LL id=cur[i]; x[id].w-=exc[e]; x[id^1].w+=exc[e]; exc[i]-=exc[e]; if(x[id].w==0) u=i;//退回到容量為0的弧尾 } } for(LL i=cur[u]; i!=-1; i=x[i].next) //從當前弧開始尋找允許弧 { LL v=x[i].v; if(x[i].w>0&&dis[u]==dis[v]+1)//找到允許弧 { flag=0;//找到允許弧 cur[u]=i;//更新當前弧 pre[v]=u;//記錄前驅 exc[v]=min(exc[u],x[i].w);//計算最大增廣 u=v; break; } } if(flag)//沒有找到允許弧 { if(--gap[dis[u]]==0) break;//出現斷層結束 LL minn=N; cur[u]=first[u]; for(LL i=first[u]; i!=-1; i=x[i].next) //找離當前點可到達的最小層次 { LL v=x[i].v; if(x[i].w>0&&dis[v]<minn) { minn=dis[v]; cur[u]=i;//修改當前弧標記 } } dis[u]=minn+1;//更新該點層次 gap[dis[u]]++;//層次點數++ if(u!=s) u=pre[u];//回溯繼續尋找允許弧 } } return Max_flow; } void init() { memset(first,-1,sizeof(first)); cont=0; } LL X[110][110]; LL z[4][2]={1,0,0,1,-1,0,0,-1}; LL vis[10010]; int main() { LL ncase; scanf("%lld",&ncase); for(int k=1;k<=ncase;k++) { init(); scanf("%lld%lld",&n,&m); LL s=0ll,t=n-1; for(int i=0;i<m;i++) { LL a,b,c,d; scanf("%lld%lld%lld%lld",&a,&b,&c,&d); add_edge(a,b,c); if(d) { add_edge(b,a,c); } } LL ans=SAP(s,t,t+1);//求出最大流 for(int i=0;i<cont;i+=2)//最小割一定是流量一定是滿的,但滿的邊不一定是最小割 { if(x[i].w==0)//滿的邊賦值為1 { x[i].w=1; //x[i^1].w=0; } else//否則inf { x[i].w=INF; //x[i^1].w=0; } } ans=SAP(s,t,t+1);//再一次最大流就是答案 printf("Case %d: %lld\n",k,ans);//還有一種方法,給每條邊的容量*比較大的值+1最後對這個數取餘就是答案 } }

題目連結戳這裡
題目大致意思就是給你一個圖讓求從0到n-1最少需要割斷幾條邊(在最小割的基礎上)。輸入每組4個數a,b,c,d.代表從a到b權值為c,d為1代表無向邊,0代表有向邊。