模板題
這三種算法基於尋找增廣路徑來求解最大流,當一個網絡圖中不存在增廣路徑時我們就得到了最大流;
1.EK:
//直接使用bfs找最近的增廣路徑,每找到一條就更新殘餘網絡,然後繼續找,直到不存在爲止,這個真好懂^ V ^
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<cstring>
#include<queue>
const int maxn = 10000+10;
const int maxm = 100000 + 10;
const int inf_max = 0x3f3f3f3f;
using namespace std;
typedef long long ll;
int n,m,head[maxn],cnt,flow[maxn];
struct EDG {
int w,v,nxt,id;
EDG() {}
EDG(int tv,int tw,int tn) {w=tw,v=tv,nxt=tn;}
}edge[maxm<<1],pre[maxn];
void Initial() {
memset(head,-1,sizeof(head));
cnt = 0;
}
void add_edge(int u,int v,int w) {
edge[cnt] = EDG(v,w,head[u]);
head[u] = cnt++;
}
int bfs(int s,int e) {
for(int i = 1;i <= n; ++i) pre[i].v = -1,flow[i] = 0;
flow[s] = inf_max;
queue<int>q;
while(!q.empty()) q.pop();
q.push(s);
while(!q.empty()) {
int u = q.front();q.pop();
// printf("u:%d %d\n",u,q.size());
if(u == e) return flow[e];
for(int i = head[u]; ~i; i= edge[i].nxt) {
int v = edge[i].v,w = edge[i].w;
if(w && pre[v].v == -1) {
flow[v] = min(w,flow[u]);
q.push(v);pre[v].v = u;pre[v].id = i;
}
}
}
return -1;
}
int Maxflow(int s,int e) {
int addflow,ret = 0;
while((addflow = bfs(s,e)) != -1) {
ret += addflow;
//cout<<addflow<<endl;
int k = e;
while(k != s) {
edge[pre[k].id].w -= addflow;
edge[pre[k].id^1].w += addflow;
k = pre[k].v;
}
}
return ret;
}
int main()
{
int s,e;
scanf("%d%d%d%d",&n,&m,&s,&e);
Initial();
for(int i = 1;i <= m; ++i) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(u == v) continue;
add_edge(u,v,w),add_edge(v,u,0);
}
cout<<Maxflow(s,e)<<endl;
return 0;
}
2.isap:
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<cstring>
#include<queue>
const int maxn = 10000+10;
const int maxm = 120000 + 10;
const int inf_max = 0x3f3f3f3f;
using namespace std;
typedef long long ll;
//最大流:EK,sap,dinic
int n,m,head[maxn],cnt,num[maxn],h[maxn],s,cur[maxn],e;
struct EDG{
int v,w,nxt;
EDG() {}
EDG(int tv,int tw,int tn) {v=tv,w=tw,nxt=tn;}
}edge[maxm << 1];
void Initial() {
memset(head,-1,sizeof(head));
memset(h,0,sizeof(h));
cnt = 0;
}
void add_edge(int u,int v,int w) {
edge[cnt] = EDG(v,w,head[u]);
head[u] = cnt++;
}
int dfs(int now,int flow) {
int sum = 0;
if(now == e) return flow;
for(int i = cur[now]; ~i;i = edge[i].nxt) {
int v = edge[i].v,w = edge[i].w;
if(w && h[now] == h[v] + 1) {
cur[now] = i;
int tmp = dfs(v,min(flow-sum,w));
edge[i].w -= tmp;edge[i^1].w += tmp;
sum += tmp;
if(flow == sum) return sum;
}
}
if(h[s] >= n) return sum;
num[h[now]]--;
if(num[h[now]] == 0) h[s] = n;
h[now]++;num[h[now]]++;cur[now] = head[now];
return sum;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&e);
Initial();
for(int i = 1;i <= m; ++i) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(u == v) continue;
add_edge(u,v,w);add_edge(v,u,0);
}
num[0] = n;
int ans = 0;
for(int i = 1;i <= n; ++i) cur[i] = head[i];
while(h[s] < n) {
ans += dfs(s,inf_max);
}
cout<<ans<<endl;
return 0;
}
3.dinic
//這個就是sap算法中回溯更新點的過程搞了一個單獨的BFS來執行;然後這個每次的BFS更新距離標號數組(emm,和sap的差不多,只是這個是到源點的),使得原圖成爲了一個層次圖,然後在層次圖中用dfs尋找增廣路。
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<cstring>
#include<queue>
const int maxn = 10000+10;
const int maxm = 100000 + 10;
const int inf_max = 0x3f3f3f3f;
using namespace std;
typedef long long ll;
//Dinic
int n,m,s,e,head[maxn],cnt,h[maxn];
struct EDG{
int v,w,nxt;
EDG() {}
EDG(int tv,int tw,int tn) {v=tv,w=tw,nxt=tn;}
}edge[maxm<<1];
void Initial() {
cnt = 0;
memset(head,-1,sizeof(head));
}
void add_edge(int u,int v,int w) {
edge[cnt] = EDG(v,w,head[u]);
head[u] = cnt++;
}
bool bfs(int now) {
memset(h,-1,sizeof(h));
h[now] = 0;
queue<int>q;
while(!q.empty()) q.pop();
q.push(now);
while(!q.empty()) {
int u = q.front();q.pop();
if(u == e) return true;
for(int i = head[u];~i;i = edge[i].nxt) {
int v = edge[i].v,w = edge[i].w;
if(w && h[v] == -1) {
h[v] = h[u] + 1;
q.push(v);
}
}
}
return false;
}
int dfs(int now,int flow) { //now表示當前點,flow表示流入當前點的流量,返回實際流入當前點的流量,即增廣路徑的流量
if(now == e) return flow;
int sum = 0;
for(int i = head[now]; ~i ; i =edge[i].nxt) {
int v = edge[i].v,w = edge[i].w;
if(w && h[v] == h[now] + 1) { //如果可能形成增廣路徑
int tmp = dfs(v,min(flow - sum,w));
sum += tmp;
edge[i].w -= tmp;
edge[i^1].w += tmp;
}
}
return sum;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&e);
Initial();
for(int i = 1;i <= m;++i) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);add_edge(v,u,0);
}
int maxflow = 0;
while(bfs(s)) {
maxflow += dfs(s,e);
}
cout<<maxflow<<endl;
return 0;
}
dinic和isap都引入了層次圖的概念,每次尋找增廣路徑都是最短的。
最小費用最大流:
把EK中的bfs換成spfa,也就是在找增廣路徑的時候找花費最小的增廣路。
//#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<cstring>
#include<queue>
const int maxn = 5e3+10;
const int maxm = 5e4 + 10;
const int inf_max = 0x3f3f3f;
using namespace std;
typedef long long ll;
int n,m,s,t,head[maxn],cnt,last[maxn],pre[maxn],dis[maxn],vis[maxn],flow[maxn],maxflow,mincost;
struct EDG{
int v,nxt,w,cost;
EDG() {}
EDG(int tv,int tn,int tw,int tc) {v=tv,nxt=tn,w=tw,cost=tc;}
}edge[maxm<<1];
void Initial() {
memset(head,-1,sizeof(head));
cnt = 0;
}
void add_edge(int u,int v,int w,int cost) {
edge[cnt] = EDG(v,head[u],w,cost);
head[u] = cnt++;
}
bool spfa(int start) {
memset(vis,0,sizeof(vis));
memset(dis,inf_max,sizeof(dis));
memset(pre,-1,sizeof(pre));
memset(flow,inf_max,sizeof(flow));
queue<int>q;
while(!q.empty()) q.pop();
q.push(start);dis[start] = 0;vis[start] = 1;
while(!q.empty()) {
int u = q.front();q.pop();vis[u] = 0;
for(int i = head[u]; ~i;i = edge[i].nxt) {
int v = edge[i].v,w = edge[i].w,c = edge[i].cost;
if(w > 0 && dis[v] > c + dis[u]) {
pre[v] = u;last[v] = i;dis[v] = c + dis[u];flow[v] = min(w,flow[u]);
if(!vis[v]) {
q.push(v);vis[v] = 1;
}
}
}
}
return (pre[t] != -1);
}
void EK() {
while(spfa(s)) {
int k = t;
maxflow += flow[t];
mincost += flow[t] * dis[t];
while(k != s) {
edge[last[k]].w -= flow[t];
edge[last[k]^1].w += flow[t];
k = pre[k];
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
Initial();
for(int i = 1;i <= m; ++i) {
int u,v,c,w;
scanf("%d%d%d%d",&u,&v,&w,&c);
add_edge(u,v,w,c);add_edge(v,u,0,-c);
}
EK();
printf("%d %d\n",maxflow,mincost);
return 0;
}