就我2018年暑假這陣子練過的區域賽題目來看node
const int N=1e3+10; const int M=2*N; int tot,head[N]; void init(){ tot=0;memset(head,-1,sizeof head); } struct Edge{ int to,next; }edge[M]; void addedge(int u,int v){ edge[tot].to=v;edge[tot].next=head[u]; head[u]=tot++; }
bool vis[N]; int ind[N]; int que[N]; void topo(int root){ int q=0,p=0;//隊列指針 que[q++]=root; while(p<q){ int u=que[p++]; for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(!vis[v]){ ind[v]--; if(ind[v]==0){ vis[v]=true; que[q++]=v; } } } } }
使用DAG的拓撲序求最短路ios
實際上就是BF的鬆弛操做,不過因爲DAG的性質,能夠下降複雜度到m。而且能夠處理負邊權。c++
int dist[N]; Rep(i,1,n)dist[i]=INF; dist[s]=0; for(int i=0;i<n;i++){ int v; for(int j=head[que[i]];~j;j=edge[j].next){ v=edge[j].to; if(dist[v]>dist[u]+edge[j].w)dist[v]=dist[u]+edge[j].w; } }
前兩個kuangbin 模板有web
首先咱們來考慮一下DAG的最小樹形圖算法
對於每一個點來講,連通這個點的最小花費,就是找一條最小的邊到這個點。網絡
思考一下DAG和拓撲排序,按照拓撲序來考慮每個點,其都能經過選擇任何一條前驅邊使這個點連通。因此咱們選擇最小的那條邊。svg
因此就使用拓撲序遍歷全部遍,而後去更新連通每一個點的貢獻函數
若是不是DAG的話,除非能縮點,不然只能用下面的板子了。spa
//來自kuangbin的最小樹形圖模版: //UVA - 11183 //最小樹形圖 求有向圖的最小生成樹 //複雜度 O(VE)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 1000+10; const int MAXM = 40000+10; struct Edge{ //分別爲起點,終點,花費 int u,v,cost; }; Edge edge[MAXM]; //pre[i]表示i節點的入邊的起點,in[i]表示該邊的權值 int pre[MAXN],id[MAXN],visit[MAXN],in[MAXN]; //root爲根節點,n爲節點數量,m爲邊數量 int zhuliu(int root,int n,int m,Edge edge[]){ //最小樹形圖的總權值 int res=0,u,v; while(1){ //找每一個節點的最小入邊 //初始化全部入邊邊權無窮大 for(int i=0;i<n;i++) in[i]=INF; //對於每一個邊 for(int i=0;i<m;i++) //若是該邊不是自環邊,該邊的終點v頂點的入邊邊權比這條邊大,那麼這條邊做爲v頂點的入邊 if(edge[i].u!=edge[i].v && edge[i].cost<in[edge[i].v]){ //入邊的起始頂點 pre[edge[i].v]=edge[i].u; //入邊的邊權 in[edge[i].v]=edge[i].cost; } //若是有除根節點之外的點的入邊邊權無窮大,那麼不存在最小樹形圖 for(int i=0;i<n;i++) if(i!=root && in[i]==INF) return -1; //找環 //環的數量 int tn=0; memset(id,-1,sizeof(id)); memset(visit,-1,sizeof(visit)); in[root]=0; //標記每一個環 for(int i=0;i<n;i++){ //記錄權值 res+=in[i]; v=i; //尋找v節點所在的環 //visit保證不會無限循環,而且用i標記了該環是那個全部的頂點 while(visit[v]!=i && id[v]==-1 && v!=root){ visit[v]=i; v=pre[v]; } //標記環上的頂點是屬於第tn個環 if(v!=root && id[v]==-1){ for(int u=pre[v];u!=v;u=pre[u]) id[u]=tn; id[v]=tn++; } } //無環,當前生成樹就是最小樹形圖 if(tn==0) break; //有環創建新圖 for(int i=0;i<n;i++) if(id[i]==-1) id[i]=tn++; for(int i=0;i<m;){ v=edge[i].v; //用環號代替起始點,邊是創建在兩個還之間 edge[i].u=id[edge[i].u]; edge[i].v=id[edge[i].v]; //i邊的權值要減去v所在環的入邊權值 if(edge[i].u!=edge[i].v) edge[i++].cost-=in[v]; //i邊在鏈接的是同一個環裏面的連個節點,該邊捨去 else swap(edge[i],edge[--m]); } //對新圖求最小樹形圖 //新圖的節點數量 n=tn; //新圖的根節點位置,縮點所在的位置 root=id[root]; } //最小樹形圖的權值 return res; } int g[MAXN][MAXN]; int main(){ int n,m; int t; scanf("%d",&t); for(int casei=1;casei<=t;casei++){ scanf("%d%d",&n,&m); //初始化 for(int i=0;i<n;i++) for(int j=0;j<n;j++) g[i][j]=INF; int u,v,cost; while(m--){ scanf("%d%d%d",&u,&v,&cost); if(u==v)continue; g[u][v]=min(g[u][v],cost); } int L=0; for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(g[i][j]<INF){ edge[L].u=i; edge[L].v=j; edge[L++].cost=g[i][j]; } //參數分別爲根節點,總結點數邊數,邊集合 int ans=zhuliu(0,n,L,edge); printf("Case #%d: ",casei); if(ans==-1) printf("Possums!\n"); else printf("%d\n",ans); } }
針對有向圖的一個支配集,固然也不算支配集,
從這個點集合出發,能夠到達有向圖的任意一個點。稱做爲一個點基本debug
咱們只要從全部的最高強連通份量中各選一個點組成集合就能組成 **最小點基 **
取權值最小的就是最小權點基
縮點後從新建邊就能夠了
dij mlogn
spfa mk
floyd n^3
Floyd求最小環
Floyd基於鬆弛的動態規劃
針對每個k去鬆弛i-j的路徑。
for(k,1,n)for(i,1,n)for(j,1,n)if(可鬆弛)鬆弛
最小環爲負就是有負環
const int MAXN = 110; const int INF = 0xffffff0; int temp,Map[MAXN][MAXN],Dist[MAXN][MAXN],pre[MAXN][MAXN],ans[MAXN*3]; void Solve(int i,int j,int k) { temp = 0; //回溯,存儲最小環 while(i != j) { ans[temp++] = j; j = pre[i][j]; } ans[temp++] = i; ans[temp++] = k; } void Floyd(int N) { for(int i = 1; i <= N; ++i) for(int j = 1; j <= N; ++j) { Dist[i][j] = Map[i][j]; pre[i][j] = i; } int MinCircle = INF; //最小環 for(int k = 1; k <= N; ++k) { for(int i = 1; i <= N; ++i) { for(int j = 1; j <= N; ++j) { if(i != j && Dist[i][j] != INF && Map[i][k] != INF && Map[k][j] != INF && Dist[i][j] + Map[i][k] + Map[k][j] < MinCircle) { MinCircle = min(MinCircle, Dist[i][j] + Map[i][k] + Map[k][j]); Solve(i,j,k); //回溯存儲最小環 } } } for(int i = 1; i <= N; ++i) { for(int j = 1; j <= N; ++j) { if(Dist[i][k] != INF && Dist[k][j] != INF && Dist[i][k] + Dist[k][j] < Dist[i][j]) { Dist[i][j] = Dist[i][k] + Dist[k][j]; pre[i][j] = pre[k][j]; //記錄點i到點j的路徑上,j前邊的點 } } } } if(MinCircle == INF) //不存在環 { printf("No solution.\n"); return; } //若是求出最小環爲負的,原圖一定存在負環 for(int i = 0;i < temp; ++i) //輸出最小環 if(i != temp-1) printf("%d ",ans[i]); else printf("%d\n",ans[i]); }
使用spfa+A*能夠處理負邊權
#include<iostream> #include<cstring> #include<queue> using namespace std; const int maxn=100010; int n,m,dis[maxn]; int tot,head1[maxn],head2[maxn]; bool flag[maxn]; struct edge{ int to,next,w; }e[maxn*2],e2[maxn*2]; struct node{ int from,f,g; bool operator < (node rhs)const{ return rhs.f==f?g>rhs.g:f>rhs.f; } }; void init(){ memset(head1,-1,sizeof head1); memset(head2,-1,sizeof head2); tot=0; memset(flag,false,sizeof flag); } void add_edge(int u,int v,int w) { e[tot].to=v; e[tot].w=w; e[tot].next=head1[u]; head1[u]=tot; e2[tot].to=u;//建反圖 e2[tot].w=w; e2[tot].next=head2[v]; head2[v]=tot; tot++; } void spfa(int t)//反圖預處理dis { for(int i=1;i<=n;i++)dis[i]=maxn; dis[t]=0; queue<int> q;q.push(t); flag[t]=1; while(!q.empty()) { int v=q.front(); q.pop();flag[v]=0; for(int i=head2[v];~i;i=e2[i].next) if(dis[e2[i].to]>dis[v]+e2[i].w) { dis[e2[i].to]=dis[v]+e2[i].w; if(!flag[e2[i].to]) { q.push(e2[i].to); flag[e2[i].to]=1; } } } } int a_star(int s,int t,int k) { if(s==t) return 0; if(dis[s]==maxn) return -1; priority_queue<node> q; int cnt=0; node tmp,to; tmp.from=s; tmp.g=0; tmp.f=tmp.g+dis[tmp.from]; q.push(tmp); while(!q.empty()) { tmp=q.top(); q.pop(); if(tmp.from==t) cnt++; if(cnt==k) return tmp.g; for(int i=head1[tmp.from];i;i=e[i].next) { to.from=e[i].to; to.g=tmp.g+e[i].w; to.f=to.g+dis[to.from]; q.push(to); } } return -1; } int main() { int x,y,z,s,t,k; cin>>n>>m; for(int i=1;i<=m;i++) { cin>>x>>y>>z; add_edge(x,y,z); } cin>>s>>t>>k;//輸入起點,終點,k短路 spfa(t); int ans=a_star(s,t,k); cout<<ans; return 0; }
支配集,就是支配全部的點。
點覆蓋,就是覆蓋全部的邊。
獨立集,就是集合內全部點互相獨立。
首先是深度優先遍歷,獲得遍歷序列。代碼以下:
int p[maxn]; bool select[maxn]; int newpos[maxn]; int now; int n,m; void DFS(int x) { newpos[now++]=x; int k; for(k=head[x];k!=-1;k=edge[k].next) { if(!select[edge[k].to]) { select[edge[k].to]=true; p[edge[k].to]=x; DFS(edge[k].to); } } }
對於最小支配集,貪心函數以下:
int greedy() { bool s[maxn]; bool set[maxn]={0}; int ans=0; int i; for(i=n-1;i>=0;i--) { int t=newpos[i]; if(!s[t]) { if(!set[p[t]]) { set[p[t]]=true; ans++; } s[t]=true; s[p[t]]=true; s[p[p[t]]]=true; } } return ans; }
對於最小點覆蓋,貪心函數以下:
int greedy() { bool s[maxn]={0}; bool set[maxn]={0}; int ans=0; int i; for(i=n-1;i>=1;i--) { int t=newpos[i]; if(!s[t]&&s[p[t]]) { set[p[t]]=true; ans++; s[t]=true; s[p[t]]=true; } } return ans; }
對於最大獨立集,貪心函數以下:
int greedy() { bool s[maxn]={0}; bool set[maxn]={0}; int ans=0; int i; for(i=n-1;i>=0;i--) { int t=newpos[i]; if(!s[t]) { set[t]=true; ans++; s[t]=true; s[p[t]]=true; } } return ans; }
最小支配集:
void DP(int u,int p) { dp[u][2]=0; dp[u][0]=1; bool s=false; int sum=0,inc=INF; int k; for(k=head[u];k!=-1;k=edge[k].next) { int to=edge[k].to; if(to==p)continue; DP(to,u); dp[u][0]+=min(dp[to][0],min(dp[u][1],dp[u][2])); if(dp[to][0]<=dp[to][1]) { sum+=dp[to][0]; s=true; } else { sum+=dp[to][1]; inc=min(inc,dp[to][0]-dp[to][1]); } if(dp[to][1]!=INF&&dp[u][2]!=INF)dp[u][2]+=dp[to][1]; else dp[u][2]=INF; } if(inc==INF&&!s)dp[u][1]=INF; else { dp[u][1]=sum; if(!s)dp[u][1]+=inc; } }
最小點覆蓋:
void DP(int u,int p) { dp[u][0]=1; dp[u][1]=0; int k,to; for(k=head[u];k!=-1;k=edge[k].next) { to=edge[k].to; if(to==p)continue; DP(to,u); dp[u][0]+=min(dp[to][0],dp[to][1]); dp[u][1]+=dp[to][0]; } }
最大獨立集:
void DP(int u,int p) { dp[u][0]=1; dp[u][1]=0; int k,to; for(k=head[u];k!=-1;k=edge[k].next) { to=edge[k].to; if(to==p)continue; DP(to,u); dp[u][0]+=dp[u][1]; dp[u][1]+=max(dp[to][0],dp[to][1]); } }
參考kuangbin模板
匈牙利算法
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<map> #include<vector> #include<queue> using namespace std; #define MAXL 100000 #define MAX 500 #define INF 1000000000 inline int read() { int x=0,t=1;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=-1,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*t; } int n1,n2; struct Line { int v,next,w; }e[MAXL]; int h[MAX],cnt; inline void Add(int u,int v,int w) { e[cnt]=(Line){v,h[u],w};h[u]=cnt++; e[cnt]=(Line){u,h[v],0};h[v]=cnt++; } int n,S,T; int level[MAX]; bool BFS() { memset(level,0,sizeof(level)); queue<int> Q; Q.push(S);level[S]=1; while(!Q.empty()) { int u=Q.front();Q.pop(); for(int i=h[u];i!=-1;i=e[i].next) { int v=e[i].v; if(e[i].w&&!level[v]) level[v]=level[u]+1,Q.push(v); } } return level[T]; } int cur[MAX]; int DFS(int u,int flow) { if(u==T||!flow)return flow; int ret=0; for(int &i=cur[u];i!=-1;i=e[i].next) { int v=e[i].v; if(e[i].w&&level[v]==level[u]+1) { int d=DFS(v,min(flow,e[i].w)); ret+=d;flow-=d; e[i].w-=d;e[i^1].w+=d; } } if(!ret)level[u]=0; return ret; } int Dinic() { int ret=0; while(BFS()) { for(int i=S;i<=T;++i)cur[i]=h[i]; ret+=DFS(S,INF); } return ret; } int main() { freopen("flyer.in","r",stdin); freopen("flyer.out","w",stdout); n=read();n1=read();n2=n-n1; S=0;T=n+1; memset(h,-1,sizeof(h)); for(int i=1;i<=n1;++i)Add(S,i,1); for(int i=n1+1;i<=n;++i)Add(i,T,1); int u,v; while(scanf("%d%d",&u,&v)!=EOF) Add(u,v,1); printf("%d\n",Dinic()); return 0; }
int head[MAXN]; int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN]; void init() { tol = 0; memset(head,-1,sizeof(head)); } //加邊,單向圖三個參數,雙向圖四個參數 void addedge(int u,int v,int w,int rw=0) { edge[tol].to =v;edge[tol].cap = w;edge[tol].next = head[u]; edge[tol].flow= 0;head[u] = tol++; edge[tol].to =u;edge[tol].cap = rw;edge[tol].next = head[v]; edge[tol].flow= 0;head[v]=tol++; } //輸入參數:起點、終點、點的總數 //點的編號沒有影響,只要輸入點的總數 int sap(int start,int end,int N) { memset(gap,0,sizeof(gap)); memset(dep,0,sizeof(dep)); memcpy(cur,head,sizeof(head)); int u = start; pre[u] = -1; gap[0] = N; int ans = 0; while(dep[start] < N) { if(u == end) { int Min = INF; for(int i = pre[u];i != -1; i = pre[edge[i^1].to]) if(Min > edge[i].cap - edge[i].flow) Min = edge[i].cap - edge[i].flow; for(int i = pre[u];i != -1; i = pre[edge[i^1].to]) { edge[i].flow += Min; edge[i^1].flow -= Min; } u = start; ans += Min; continue; } bool flag = false; int v; for(int i = cur[u]; i != -1;i = edge[i].next) { v = edge[i].to; if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u]) { flag = true; cur[u] = pre[v] = i; break; } } if(flag) { u = v; continue; } int Min = N; for(int i = head[u]; i != -1;i = edge[i].next) if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) { Min = dep[edge[i].to]; cur[u] = i; } gap[dep[u]]--; if(!gap[dep[u]])return ans; dep[u] = Min+1; gap[dep[u]]++; if(u != start) u = edge[pre[u]^1].to; } return ans; }
上下界網絡流
#include <algorithm> #include <cstring> #include <cstdio> #include <queue> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 110; namespace ISAP { const int MAXV = MAXN; const int MAXE = ( MAXV*MAXV/2 + MAXV*2 )*3; struct Edge { int u, v, c, f; Edge(){} Edge( int u, int v, int c, int f ): u(u),v(v),c(c),f(f){} }edge[MAXE<<1]; int n, m, s, t, ss, tt; int head[MAXV], nxt[MAXE<<1], eid[MAXE<<1], eidx; void init( int n2, int ss2, int tt2 ) { // 初始化,設置附加源和附加匯 n = n2; ss = ss2; tt = tt2; m = eidx = 0; memset( head, -1, sizeof(head) ); } int adde( int u, int v, int c ) { // 添加一條只有上界的邊 int rtn = m; eid[eidx] = m; nxt[eidx] = head[u]; head[u] = eidx++; edge[m++] = Edge(u,v,c,0); eid[eidx] = m; nxt[eidx] = head[v]; head[v] = eidx++; edge[m++] = Edge(v,u,0,0); return rtn; } int adde2( int u, int v, int b, int c ) { // 添加一條有上下界的邊,返回邊的下標 int rtn = adde(u,v,c-b); adde(ss,v,b); adde(u,tt,b); return rtn; } // 如下ISAP板子 int prev[MAXV], dist[MAXV], num[MAXV], cur[MAXV], res[MAXV]; queue<int> bfsq; void bfs() { for( int i = 1; i <= n; ++i ) dist[i] = n; dist[t] = 0; bfsq.push(t); while( !bfsq.empty() ) { int u = bfsq.front(); bfsq.pop(); for( int i = head[u]; ~i; i = nxt[i] ) { Edge &e = edge[eid[i]]; if( dist[e.v] == n ) { dist[e.v] = dist[u] + 1; bfsq.push(e.v); } } } } void augment() { int u = t, flow = res[t]; while( u != s ) { int i = prev[u]; edge[i].f += flow; edge[i^1].f -= flow; u = edge[i].u; } } bool advance( int &u ) { for( int i = cur[u]; ~i; i = nxt[i] ) { Edge &e = edge[eid[i]]; if( e.c > e.f && dist[e.v] + 1 == dist[u] ) { prev[e.v] = cur[u] = i; res[e.v] = min( res[u], e.c - e.f ); u = e.v; return true; } } return false; } bool retreat( int &u ) { if( --num[dist[u]] == 0 ) return false; int newd = n; for( int i = head[u]; ~i; i = nxt[i] ) { Edge &e = edge[eid[i]]; if( e.c > e.f ) newd = min( newd, dist[e.v] + 1 ); } ++num[ dist[u] = newd ]; cur[u] = head[u]; if( u != s ) u = edge[prev[u]].u; return true; } int solve( int s2, int t2 ) { // 以s2爲源,t2爲匯跑最大流 s = s2; t = t2; bfs(); for( int i = 1; i <= n; ++i ) cur[i] = head[i], ++num[dist[i]]; int u = s, flow = 0; res[s] = INF; while( dist[s] < n ) { if( u == t ) { augment(); flow += res[t]; u = s; } if( !advance(u) ) if( !retreat(u) ) break; } return flow; } } int n, s, t, ss, tt; // 點的個數,源,匯,附加源,附加匯 namespace Solve { using ISAP::head; using ISAP::nxt; using ISAP::eid; using ISAP::Edge; using ISAP::edge; bool first; void dfs( int u ) { // dfs輸出方案 // printf( "Debug: u = %d\n", u ); if( !first ) putchar(' '); first = false; printf( "%d", u ); for( int i = head[u]; ~i; i = nxt[i] ) { Edge &e = edge[eid[i]]; if( e.v <= n && e.f > 0 ) { // 任選一條邊走下去 // printf( "going eid = %d, from %d to %d, flow_left = %d\n", eid[i], e.u, e.v, e.f ); --e.f; dfs(e.v); return; } } } void addbound() { // 把每條邊流量加上下界,恢復成原圖的樣子,方便輸出方案 using ISAP::m; for( int i = 0; i < m; ++i ) { Edge &e = edge[eid[i]]; if( e.u <= n && e.v <= n && e.c > 0 ) ++e.f; } } } namespace Debug { // 調試用QwQ void print_flow() { using ISAP::edge; using ISAP::Edge; using ISAP::eid; using ISAP::m; for( int i = 0; i < m; ++i ) { Edge &e = edge[eid[i]]; if( e.u <= n && e.v <= n && e.c > 0 ) printf( "eid = %d, from %d to %d, flow = %d\n", eid[i], e.u, e.v, e.f ); } } void print_flow2() { using ISAP::edge; using ISAP::Edge; using ISAP::eid; using ISAP::m; for( int i = 0; i < m; ++i ) { Edge &e = edge[eid[i]]; if( e.f > 0 ) printf( "eid = %d, from %d to %d, flow = %d\n", eid[i], e.u, e.v, e.f ); } } } int main() { while( scanf( "%d", &n ) == 1 ) { s = n+1, t = n+2, ss = n+3, tt = n+4; ISAP::init(tt,ss,tt); for( int i = 1; i <= n; ++i ) { int mi; scanf( "%d", &mi ); while( mi-- ) { int v; scanf( "%d", &v ); ISAP::adde2(i,v,1,INF); } ISAP::adde2(s,i,0,INF); ISAP::adde2(i,t,0,INF); } int flow1 = ISAP::solve(ss,tt); // printf( "flow1 = %d\n", flow1 ); // Debug::print_flow(); // Debug::print_flow2(); int tsedge = ISAP::adde2(t,s,0,INF); // 存儲弧<t,s>的信息,調試用QwQ int ans = ISAP::solve(ss,tt); // printf( "t_s flow = %d\n", ISAP::edge[tsedge].f ); // Debug::print_flow(); // Debug::print_flow2(); printf( "%d\n", ans ); Solve::addbound(); // 把每條圖中的邊流量加上下界,恢復成原圖的樣子,方便輸出方案 while( ans ) { using namespace Solve; for( int i = head[s]; ~i; i = nxt[i] ) { Edge &e = edge[eid[i]]; if( e.v <= n && e.f > 0 ) { // 任選一個點dfs,輸出方案 first = true; --e.f; --ans; dfs(e.v); putchar('\n'); } } } } return 0; }
原理就是一直增廣 使用spfa(由於有負邊權)找到的s->t的一條可行最短路,直到不存在最短路了。能夠證實這樣造成的最大流費用是最小的。
最大費用修改spfa或者直接建負邊權便可。
#include<bits/stdc++.h> /* 支持重邊,因此邊數本身定,最大費用改爲負權便可。 */ using namespace std; const int MAXN=1e4+10; const int MAXM=2e5+10; const int INF=0x3f3f3f3f; struct Edge{ int to,next,cap,flow,cost; }edge[MAXM]; int tol,N; int head[MAXN],pre[MAXN],dis[MAXN]; bool vis[MAXN]; void init(int n){ N=n; tol=0; memset(head,-1,sizeof head); } void addedge(int u,int v,int cap,int cost){ Edge &e=edge[tol]; e.to=v; e.cap=cap; e.cost=cost; e.flow=0; e.next=head[u]; head[u]=tol++; Edge &ee=edge[tol]; ee.to=u; ee.cap=0; ee.cost=-cost; ee.flow=0; ee.next=head[v]; head[v]=tol++; } bool spfa(int s,int t){ //初始化 queue<int> q; for(int i=0;i<=N;i++) dis[i]=INF,vis[i]=false,pre[i]=-1; dis[s]=0;vis[s]=true;q.push(s); //鬆弛,沒有判斷負環,判負環用cnt[N]; while(!q.empty()){ int u=q.front();q.pop();vis[u]=false; for(int i=head[u];~i;i=edge[i].next){ Edge &e=edge[i];int v=e.to; if(e.cap>e.flow && dis[v]>dis[u]+e.cost){ dis[v]=dis[u]+e.cost; pre[v]=i; if(!vis[v]){ vis[v]=true; q.push(v); } } } } if(pre[t]==-1)return false; else return true; } void mcmf(int s,int t,int &cost,int &flow){ flow=0;cost=0; //找一條費用最小的可行流。增廣之。 while(spfa(s,t)){ int Min=INF; for(int i=pre[t];~i;i=pre[edge[i^1].to])if(Min>edge[i].cap-edge[i].flow){ Min=edge[i].cap-edge[i].flow; } //這裏i 是邊(u,v)的下標,讓edge[i^1].to=u for(int i=pre[t];~i;i=pre[edge[i^1].to]){ //debug //cout<<"pre="<<edge[i^1].to<<endl; edge[i].flow+=Min; edge[i^1].flow-=Min; cost+=edge[i].cost*Min; } flow+=Min; } }