HDU 3549 Flow Problem【最大流入门题】【Ford-Fulkerson算法】【Dinic算法】【ISAP算法】

时间:2022-10-29 04:30:27

Flow Problem

Time Limit: 5000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 17030    Accepted Submission(s): 8021


Problem Description
Network flow is a well-known difficult problem for ACMers. Given a graph, your task is to find out the maximum flow for the weighted directed graph.
 

Input
The first line of input contains an integer T, denoting the number of test cases.
For each test case, the first line contains two integers N and M, denoting the number of vertexes and edges in the graph. (2 <= N <= 15, 0 <= M <= 1000)
Next M lines, each line contains three integers X, Y and C, there is an edge from X to Y and the capacity of it is C. (1 <= X, Y <= N, 1 <= C <= 1000)
 

Output
For each test cases, you should output the maximum flow from source 1 to sink N.
 

Sample Input
 
 
23 21 2 12 3 13 31 2 12 3 11 3 1
 

Sample Output
 
 
Case 1: 1Case 2: 2
 

Author
HyperHexagon
 

Source


原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3549

最大流入门题:最大流问题在刘汝佳的《算法竞赛入门经典》和《算法竞赛入门经典训练指南》中均有纤细介绍。

竞赛中通常可以使用Dinic算法和ISAP算法,但是Ford-Fulkerson算法理解起来简单一点。

最大流问题吧算法代码当做模板,根据具体问题去建图就可以了。

AC代码1.Ford-Fulkerson算法

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn=20;
const int maxm=1050;
const int INF=0x3f3f3f3f;

int t;
int n,m;
bool vis[maxn];
int Case=0;

struct node
{
int to,cap;
unsigned int rev;
};
vector<node> G[maxn];

void add_edge(int from,int to,int cap)
{
G[from].push_back({to,cap,G[to].size()});
G[to].push_back({from,0,G[from].size()-1});
}

int dfs(int v,int t,int f)
{
if(v==t) return f;
vis[v]=true;
for(unsigned int i=0;i<G[v].size();i++)
{
node &e=G[v][i];
if(!vis[e.to]&&e.cap>0)
{
int d=dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}

int max_flow(int s,int t)
{
int flow=0;
while(1)
{
memset(vis,0,sizeof(vis));
int f=dfs(s,t,INF);
if(f==0) return flow;
flow+=f;
}
}

int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) G[i].clear();
for(int i=0;i<m;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
add_edge(x,y,c);
}
printf("Case %d: %d\n",++Case,max_flow(1,n));
}
return 0;
}

AC代码2:Dinic算法

/**
* 行有余力,则来刷题!
* 博客链接:http://blog.csdn.net/hurmishine
* 个人博客网站:http://wuyunfeng.cn/
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=15+5;
const int INF=0x3f3f3f3f;
struct Edge
{
int from,to,cap,flow;//起点,终点,容量,流量
Edge(){}
Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){}
};
struct Dinic
{
int n,m,s,t; //节点数,边数(包括反向弧),源点编号和汇点编号
vector<Edge>edge; //边表。edge[e]和edge[e^1]互为反向弧
vector<int>G[maxn]; //邻接表,G[i][j]表示节点i的第j条边在数组中的序号
bool vis[maxn]; //BFS使用,标记一个节点是否被遍历过
int d[maxn]; //从起点到i点的距离
int cur[maxn]; //当前弧下标

void init(int n,int s,int t)
{
this->n=n;
this->s=s;
this->t=t;
for(int i=1;i<=n;i++)
G[i].clear();
edge.clear();
}

void addEdge(int from,int to,int cap)
{
//edge.push_back((Edge){from,to,cap,0});
//edge.push_back((Edge){to,from,0,0});
edge.push_back( Edge(from,to,cap,0) );
edge.push_back( Edge(to,from,0,0) );
m=edge.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int>q;//
q.push(s);
d[s]=0;
vis[s]=true;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<G[x].size();i++)
{
Edge& e=edge[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow)//只考虑残量网络中的弧
{
vis[e.to]=true;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}

int DFS(int x,int a)
{
if(x==t||a==0)
return a;
int flow=0,f;
for(int &i=cur[x];i<G[x].size();i++)//从上次考虑的弧
{
Edge& e=edge[G[x][i]];
if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
{
e.flow+=f;
edge[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)
break;
}
}
return flow;
}
int MaxFlow()
{
int flow=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
flow+=DFS(s,INF);
}
return flow;
}
}Dinic;

int main()
{
//freopen("C:\\Users\\hncu_acm\\Desktop\\data.txt","r",stdin);
int T,n,m;
int kase=0;

cin>>T;
while(T--)
{
cin>>n>>m;
Dinic.init(n,1,n);
int u,v,w;
while(m--)
{
cin>>u>>v>>w;
Dinic.addEdge(u,v,w);
}
printf("Case %d: %d\n",++kase,Dinic.MaxFlow());
}
return 0;
}

AC代码3:ISAP算法:

//http://blog.csdn.net/x920405451x/article/details/39747081
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
#include <ctime>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
//typedef pair<int,int> pii;
#define INF 1e9
#define MAXN 10000
#define MAXM 100
const int maxn = 20;
const int mod = 1000003;
#define eps 1e-6
#define pi 3.1415926535897932384626433
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define scan(n) scanf("%d",&n)
#define scan2(n,m) scanf("%d%d",&n,&m)
#define scans(s) scanf("%s",s);
#define ini(a) memset(a,0,sizeof(a))
#define FILL(a,n) fill(a,a+maxn,n)
#define out(n) printf("%d\n",n)
//ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b);}
#define mk(n,m) make_pair(n,m)
using namespace std;
struct Edge
{
int from,to,cap,flow;
Edge(int f = 0, int t = 0, int c = 0, int fl = 0):from(f),to(t),cap(c),flow(fl) {}
};
int n,m;
vector<int> G[maxn];
vector<Edge> edges;
bool vis[maxn];
int d[maxn]; //残量网络中从节点i到汇点t的距离
int cur[maxn]; //当前弧下标
int p[maxn]; //可增广路上的上一条弧
int num[maxn]; //距离标号计数

struct ISAP
{
void init(int n)
{
edges.clear();
rep(i,n+3) G[i].clear();
FILL(d,n+1);
}
void add(int from,int to,int cap)
{
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
int p = edges.size();
G[from].push_back(p - 2);
G[to].push_back(p - 1);
}

bool bfs(int s,int t)
{
memset(vis, 0, sizeof(vis));
queue<int> q;
q.push(t);
vis[t] = 1;
d[t] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < G[u].size(); i++)
{
Edge &e = edges[G[u][i]^1]; //这条弧的反弧
if (!vis[e.from] && e.cap > e.flow)
{
vis[e.from] = true;
d[e.from] = d[u] + 1;
q.push(e.from);
}
}
}
return vis[s];
}

// 增广
int augment(int s,int t)
{
int u = t, df = INF;
// 从汇点到源点通过 p 追踪增广路径, df 为一路上最小的残量
while (u != s)
{
Edge &e = edges[p[u]];
df = min(df, e.cap - e.flow);
u = edges[p[u]].from;
}
u = t;
// 从汇点到源点更新流量
while (u != s)
{
edges[p[u]].flow += df;
edges[p[u]^1].flow -= df;
u = edges[p[u]].from;
}
return df;
}

int maxflow(int s,int t)
{
int flow = 0;
bfs(s,t);
memset(num, 0, sizeof(num));
for (int i = 0; i < n; i++) num[d[i]]++;
int u = s;
memset(cur, 0, sizeof(cur));
while (d[s] < n)
{
if (u == t)
{
flow += augment(s,t);
u = s;
}
bool advanced = false;
for (int i = cur[u]; i < G[u].size(); i++)
{
Edge& e = edges[G[u][i]];
if (e.cap > e.flow && d[u] == d[e.to] + 1)
{
advanced = true;
p[e.to] = G[u][i];
cur[u] = i;
u = e.to;
break;
}
}
if (!advanced) // retreat
{
int m = n - 1;
for (int i = 0; i < G[u].size(); i++)
{
Edge& e = edges[G[u][i]];
if (e.cap > e.flow)
m = min(m, d[e.to]);
}
if (--num[d[u]] == 0) break; // gap 优化
num[d[u] = m+1]++;
cur[u] = 0;
if (u != s)
u = edges[p[u]].from;
}
}
return flow;
}

} ISAP;

int main()
{
/**
#ifndef ONLINE_JUDGE
freopen("C:\\Users\\hncu_acm\\Desktop\\data.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
*/
int T;
cin>>T;
int cas = 1;
while(T--)
{
cin>>n>>m;
ISAP.init(n);
int u,v,c;
rep(i,m)
{
scanf("%d%d%d",&u,&v,&c);
ISAP.add(u,v,c);
}
printf("Case %d: %d\n",cas++,ISAP.maxflow(1,n));
}
return 0;
}

参考链接: http://blog.csdn.net/u013480600/article/details/38796521

http://blog.csdn.net/x920405451x/article/details/39747081

http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=31339&messageid=1&deep=0