您的位置:首页 > 理论基础 > 计算机网络

网络流入门总结(EK算法)

2015-08-04 23:33 483 查看
终于真正意义上接触网络流了,记得第一次看网络流是在去年去区域赛的火车上,但是拿着LRJ的白书,看了好久,感觉可以了,然并卵,比赛的时候就签了一下到,就走了。。。从那以后就没碰过网络流,原来等着跟大工大神学习的,好像因为各种事情一直没有上这一课。然后又把白皮看了一遍,看了很多博客,感觉其中有一个人的博客写的特别好,现在找不到了。。。

跟着专题训练写了几道模板题,照着大白书稍微改了一点,成为了自己的模板。下面做一些入门网络流的总结:

1:反向边。每一条路径都对应着一条反向边,u->v流过f的流量,那个对应的反向边v->u流过-f的流量。而对应的反向边的容量cap = 0。

2:残量网络。就是把每一条路径剩余容量标称着条边的权值。(当然反向边也是必须要算的,这个时候要注意边的数量是原来边数量的两倍,权值>0才能形成一条边)

3:增广路。如果在残余网络中有一条路径能够从s(源点)走到t(汇点)那么,我们就称这条路为增广路。

4:增广路求最大流。增广路对应的每一条路径权值的最小值(d)全部加到原网络时,这个时候我们发现总的流量增加了d,(注意在d添加到原边时,对应的反向边的流量需要-d,因为是对应的关系,很好理解)。那么问题就转化成如何找到所有的增广路。这个就很显然了,我们只要遍历一遍残量网络图,就可以找到了。(BFS实现,DFS比BFS慢一点,有一个人的博客有测试的)

下面就说一个这6道模板题:

POJ_1273 http://poj.org/problem?id=1273 很裸的最大流题,正好可以检测模板的正确性:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 210
using namespace std;

struct Edge{
int from,to,cap,flow;
Edge(){};
Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edges[N<<1];

int edge_cnt;
vector <int> G
; //存图
int pre
,a
; //记录增广路路径
int s,t,n,m;
bool vis
;

void AddEdge(int from,int to,int cap){
edges[edge_cnt++] = Edge(from,to,cap,0);
edges[edge_cnt++] = Edge(to,from,0,0);
G[from].push_back(edge_cnt - 2);
G[to].push_back(edge_cnt -1);
}

int bfs(){
memset(vis,false,sizeof(vis));
queue <int> q;
q.push(s);
a[s] = INF;
while(!q.empty()){
int u = q.front(); q.pop();
if(u == t)  break;
FOR(i,0,G[u].size()){
int e = G[u][i];
int v = edges[e].to;
if(!vis[v] && edges[e].cap > edges[e].flow){
vis[v] = true;
pre[v] = e;
a[v] = min(a[u],edges[e].cap-edges[e].flow);
q.push(v);
}
}
}
if(!vis[t]) return -1;
return a[t];
}

int EK(){
int max_flow = 0;
int tem;
while((tem = bfs()) != -1){
max_flow += tem;
for(int u = t; u != s; u = edges[pre[u]].from){
edges[pre[u]].flow += tem;
edges[pre[u]^1].flow -= tem;
}
}
return max_flow;
}

int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d",&n,&m)){
FOR(i,0,N)  G[i].clear();
s = 1; t = m;
int u,v,c;
edge_cnt = 0;
FOR(i,0,n){
scanf("%d%d%d",&u,&v,&c);
AddEdge(u,v,c);
}
printf("%d\n",EK());
}
return 0;
}


HDU_3549 http://acm.hdu.edu.cn/showproblem.php?pid=3549 也是一个模板练手题

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 1010
using namespace std;

struct Edge{
int from,to,cap,flow;
Edge(){};
Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edges[N<<1];

int edge_cnt;
vector <int> G
; //存图
int pre
,a
; //记录增广路路径
int s,t,n,m;
bool vis
;

void AddEdge(int from,int to,int cap){
edges[edge_cnt++] = Edge(from,to,cap,0);
edges[edge_cnt++] = Edge(to,from,0,0);
G[from].push_back(edge_cnt - 2);
G[to].push_back(edge_cnt -1);
}

int bfs(){
memset(vis,false,sizeof(vis));
queue <int> q;
q.push(s);
a[s] = INF;
while(!q.empty()){
int u = q.front(); q.pop();
if(u == t)  break;
FOR(i,0,G[u].size()){
int e = G[u][i];
int v = edges[e].to;
if(!vis[v] && edges[e].cap > edges[e].flow){
vis[v] = true;
pre[v] = e;
a[v] = min(a[u],edges[e].cap-edges[e].flow);
q.push(v);
}
}
}
if(!vis[t]) return -1;
return a[t];
}

int EK(){
int max_flow = 0;
int tem;
while((tem = bfs()) != -1){
max_flow += tem;
for(int u = t; u != s; u = edges[pre[u]].from){
edges[pre[u]].flow += tem;
edges[pre[u]^1].flow -= tem;
}
}
return max_flow;
}

int main()
{
//freopen("test.in","r",stdin);
int T,tCase = 0;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
FOR(i,0,N)  G[i].clear();
s = 1; t = n;
int u,v,c;
edge_cnt = 0;
FOR(i,0,m){
scanf("%d%d%d",&u,&v,&c);
AddEdge(u,v,c);
}
printf("Case %d: ",++tCase);
printf("%d\n",EK());
}
return 0;
}


POJ_1087 http://poj.org/problem?id=1087 这个题注意一下对应关系就好了,题目是插头可以变成别的种类的,不是插座可以变成别的种类的,我用的map来对应的节点,建图的时候麻烦一点。(其实也可以用二分图的匹配来写,本来二分图就可以用网络流来写的,所以不影响)在插头一边加源点,插座一边加汇点,转换器对应的路径,cap=1。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <string>
#include <map>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 440

using namespace std;

struct Str{
char s[30];
Str() {}
Str(char* ths) {FOR(i,0,30) s[i] = ths[i];}
bool operator < (const Str& rhs) const{
return strcmp(s,rhs.s) == -1 ? true : false;
}
}str
;

int n,m,k;
char src
[30],tar
[30],sr
[30],ta
[30];
int cnt,co;
int l
,r
;
bool mat

;
bool vis
;
vector <int> GP
;
map <string,int> p;

void dfs(int u){
FOR(i,0,GP[u].size()){
int v = GP[u][i];
if(!vis[v]) {vis[v] = true;dfs(v);}
}
}

///ÍøÂçÁ÷Ïà¹Ø
int s,t,edge_cnt;
int a
;

struct Edge{
int from,to,cap,flow;
Edge() {}
Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edge[N*N*2];

vector <int> G
;
int pre
;

void AddEdge(int from,int to,int cap){
edge[edge_cnt++] = Edge(from,to,cap,0);
edge[edge_cnt++] = Edge(to,from,0,0);
G[from].push_back(edge_cnt-2);
G[to].push_back(edge_cnt-1);
}

int bfs(){
memset(vis,false,sizeof(vis));
queue <int> q;
a[s] = INF;
q.push(s);
vis[s] = true;
while(!q.empty()){
int u = q.front(); q.pop();
if(u == t)  break;
FOR(i,0,G[u].size()){
int e = G[u][i];
int v = edge[e].to;
if(!vis[v] && edge[e].cap > edge[e].flow){
a[v] = min(a[u],edge[e].cap-edge[e].flow);
pre[v] = e;
vis[v] = true;
q.push(v);
}
}
}
if(!vis[t]) return -1;
return a[t];
}

int EK(){
int max_flow = 0;
int d;
while((d = bfs()) != -1){
max_flow += d;
for(int u = t;u != s;u = edge[pre[u]].from){
edge[pre[u]].flow += d;
edge[pre[u]^1].flow -= d;
}
}
return max_flow;
}

/****/

void init(){
sort(str,str+co);
cnt = 0;
p[str[0].s] = (++cnt);
FOR(i,1,co){
if(strcmp(str[i].s,str[i-1].s) != 0) p[str[i].s] = (++cnt);
}
FOR(i,0,N)  GP[i].clear();
FOR(i,0,k){
GP[p[sr[i]]].push_back(p[ta[i]]);
//printf("%d %d\n",p[sr[i]],p[ta[i]]);
//printf("%d %d\n",p[sr[i]],GP[p[sr[i]]].size());
}
memset(mat,false,sizeof(mat));
FOR(i,1,cnt+1){
memset(vis,false,sizeof(vis));
vis[i] = true;
dfs(i);
FOR(j,1,cnt+1){
if(vis[j]) mat[i][j] = true;
}
}
FOR(i,n+1,n+m+1){
l[i] = p[tar[i]];
}
FOR(i,1,n+1){
r[i] = p[src[i]];
}
s = 0;
t = n+m+1;
FOR(i,0,N)  G[i].clear();
edge_cnt = 0;
FOR(i,n+1,n+m+1){
AddEdge(s,i,1);
}
FOR(i,1,n+1){
AddEdge(i,t,1);
}
FOR(i,n+1,n+m+1){
FOR(j,1,n+1){
if(mat[l[i]][r[j]]){AddEdge(i,j,1);}
}
}
}

int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d",&n)){
co = 0;
FOR(i,1,n+1){
scanf("%s",src[i]);
str[co++] = Str(src[i]);
}
scanf("%d",&m);
char tem[30];
FOR(i,n+1,n+m+1){
scanf("%s%s",tem,tar[i]);
str[co++] = Str(tar[i]);
}
scanf("%d",&k);
FOR(i,0,k){
scanf("%s%s",sr[i],ta[i]);
str[co++] = Str(sr[i]);
str[co++] = Str(ta[i]);
}
init();
printf("%d\n",m-EK());
}
return 0;
}
POJ_1274 http://poj.org/problem?id=1274 本题是一个裸的二分图最大匹配,正好练一下二分图最大匹配的模板。网络流当然也可以写,一边加源点,一边加汇点,容量都设为1,就变成了一个最大流问题。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <algorithm>
#include <vector>
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 250

using namespace std;

vector <int> G[N<<1];
int n,m;
int march[N<<1];
bool vis[N<<1];

bool dfs(int u){
vis[u] = true;
FOR(i,0,G[u].size()){
int v = G[u][i];
if(!vis[v]){
vis[v] = true;
if(march[v] == -1 || dfs(march[v])){
march[v] = u;
march[u] = v;
return true;
}
}
}
return false;
}

int MaxMarch(){
int ans = 0;
memset(march,-1,sizeof(march));
FOR(i,1,n+1){
if(march[i] == -1){
memset(vis,false,sizeof(vis));
if(dfs(i))  ans++;
}
}
return ans;
}

int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d",&n,&m)){
FOR(i,0,(n+m+1))   G[i].clear();
int cnt;
int v;
FOR(i,1,n+1){
scanf("%d",&cnt);
while(cnt--){
scanf("%d",&v);
G[i].push_back(v+n);
G[v+n].push_back(i);
}
}
printf("%d\n",MaxMarch());
}
return 0;
}


POJ_1459 http://poj.org/problem?id=1459 题目乍一看有很多源点,汇点,其实都是假的,我们把所有源点全部连到一个s上,对应的路径cap就是这个点的cap,同理,所有的汇点都连到t上,cap对应的就是这个点的cap。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define INF (1<<30)
#define N 220

using namespace std;

inline void readint(int &ret)
{
char c;
do { c = getchar();
} while(c < '0' || c > '9');
ret = c - '0';
while((c=getchar()) >= '0' && c <= '9')
ret = ret * 10 + ( c - '0' );
}

int n,np,nc,m;

///wangluoliu xiang guan
struct Edge{
int from,to,cap,flow;
Edge(){}
Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edge[N*N];

int s,t,edge_cnt,a
,pre
;
bool vis
;
vector <int> G
;

void AddEdge(int u,int v,int c){
G[u].push_back(edge_cnt);
edge[edge_cnt++] = Edge(u,v,c,0);
G[v].push_back(edge_cnt);
edge[edge_cnt++] = Edge(v,u,0,0);
}

void init_ntf(){
FOR(i,0,N)  G[i].clear();
edge_cnt = 0;
}

int bfs(){
memset(vis,false,sizeof(vis));
queue <int> q;
q.push(s);
a[s] = INF;
vis[s] = true;
while(!q.empty()){
int u = q.front(); q.pop();
if(u == t)  break;
FOR(i,0,G[u].size()){
int e = G[u][i];
int v = edge[e].to;
if(!vis[v] && edge[e].cap > edge[e].flow){
a[v] = min(a[u],edge[e].cap - edge[e].flow);
pre[v] = e;
vis[v] = true;
q.push(v);
}
}
}
if(!vis[t]) return -1;
return a[t];
}

int EK(){
int max_flow = 0;
int d;
while((d = bfs()) != -1){
max_flow += d;
for(int u = t; u != s; u = edge[pre[u]].from){
edge[pre[u]].flow += d;
edge[pre[u]^1].flow -= d;
}
}
return max_flow;
}

int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d%d%d",&n,&np,&nc,&m)){
s = 0;
t = n+1;
int u,v,l;
init_ntf();
FOR(i,0,m){
readint(u);
readint(v);
readint(l);
AddEdge(u+1,v+1,l);
}
FOR(i,0,np){
readint(v);
readint(l);
AddEdge(s,v+1,l);
}
FOR(i,0,nc){
readint(u);
readint(l);
AddEdge(u+1,t,l);
}
printf("%d\n",EK());
}
return 0;
}


POJ_3281 http://poj.org/problem?id=3281 拆点+最大流。单纯地以为是二分图匹配肯定就贵了(反正我是没想出来二分图要怎么搞)。放到网络流里面,就会发现图建不出来!!!为什么???因为牛这个点可以经过无数遍,这个与题目要求显然不符合,这个时候要怎么办,就是把一头牛拆成牛1,牛2,对应的牛1,牛2之间连接一条容量为1的路径。这个样子就能保证每一头牛都最多走了一次。。。见图的关键就是每一条路径都与每一条路径实际一一对应!!!

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#define INF (1<<30)
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define N 440

using namespace std;

struct Edge{
int from,to,cap,flow;
Edge() {}
Edge(int u,int v,int c,int f) : from(u),to(v),cap(c),flow(f){}
}edge[N*N*2];

vector <int> G
;
int edge_cnt,pre
,a
,f,d,n,s,t;
bool vis
;

void AddEdge(int u,int v,int c){
edge[edge_cnt++] = Edge(u,v,c,0);
edge[edge_cnt++] = Edge(v,u,0,0);
G[u].push_back(edge_cnt-2);
G[v].push_back(edge_cnt-1);
}

int bfs(){
memset(vis,false,sizeof(vis));
queue <int> q;
q.push(s);
vis[s] = true;
a[s] = INF;
while(!q.empty()){
int u = q.front(); q.pop();
if(u == t)  break;
FOR(i,0,G[u].size()){
int e = G[u][i];
int v = edge[e].to;
if(!vis[v] && edge[e].cap > edge[e].flow){
a[v] = min(a[u],edge[e].cap - edge[e].flow);
pre[v] = e;
vis[v] = true;
q.push(v);
}
}
}
if(!vis[t]) return -1;
return a[t];
}

int EK(){
int max_flow = 0;
int d;
while((d = bfs()) != -1){
max_flow += d;
for(int u = t; u != s; u = edge[pre[u]].from){
edge[pre[u]].flow += d;
edge[pre[u]^1].flow -= d;
}
}
return max_flow;
}

int main()
{
//freopen("test.in","r",stdin);
while(~scanf("%d%d%d",&n,&f,&d)){
//if(n == 0 || f == 0 || d == 0)  {printf("0\n");continue;}
FOR(i,0,N)  G[i].clear();
edge_cnt = 0;
s = 0;
t = f+n+n+d+1;
FOR(i,1,n+1){
int cnt_f,cnt_d;
scanf("%d%d",&cnt_f,&cnt_d);
FOR(j,0,cnt_f){
int u;
scanf("%d",&u);
AddEdge(u,f+i,1);
}
AddEdge(f+i,f+i+n,1);
FOR(j,0,cnt_d){
int v;
scanf("%d",&v);
AddEdge(f+i+n,f+n+n+v,1);
}
}
FOR(i,1,f+1){
AddEdge(s,i,1);
}
FOR(i,f+n+n+1,t){
AddEdge(i,t,1);
}
printf("%d\n",EK());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: