网络流最大流问题
网络流最大流问题:给定有向图,有两个特殊的源点s和汇点t,每条边有一定的容量,问满足从s到t的最大流量
一些词汇的理解:
- 流量flow,是指每条边实际流过的量
- 容量capacity,是指每条边可以通过的最大流量
- 残量residual,是指每条边的上的容量和流量之差
最大流基本算法——增广路算法
增广:从源点到汇点的某一条路,每条边的残量取最小值d,这个最小值就是这条路上每条边还能够增加的流量。给这条路上的每个边增加d流量,这个过程就叫做增广。
增广路算法思路:找任意路径,看看是否能够增广,直到s-t不存在可以增广,这个时候的流量就是最大流。找寻从s到t的任意路径可以使用dfs也可以用bfs,但是dfs很容易超时,一般采用bfs。
代码:(来源于紫书)
struct Edge
{
int from, to, cap, flow;//入点,出点,容量,流量
Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f) {}//初始化
};
struct EdmondsKarp
{
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
int a[maxn];
int p[maxn];
void init(int n)//初始化
{
for(int i = 0; i < n; i++)
G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap)//添加边
{
edges.push_back(Edge(from, to, cap, 0));//正向偶数
edges.push_back(Edge(to, from, 0, 0));//负向奇数
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
int Maxflow(int s, int t)//最大流bfs
{
int flow = 0;//初始流量为0
for(;;)
{
memset(a, 0, sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = inf;//和该点连接的上一条边的可增广的量
while(!Q.empty())
{
int x = Q.front();
Q.pop();
for(int i = 0; i < G[x].size(); i++)
{
Edge &e = edges[G[x][i]];
if(!a[e.to] && e.cap > e.flow)
{
p[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);//取到路上各个边的最小残量
Q.push(e.to);
}
}
if(a[t])//这条路已经给终点增广到了
break;
}
if(!a[t])//如果汇点不能再有增了,已经是最大流了
break;
for(int u = t; u != s; u = edges[p[u]].from)//流量要发生改变
{
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];//异或是根据之前添加的奇偶对应正反边
}
flow += a[t];
}
return flow;
}
};
CSU2073: Tile Cut
参考:https://blog.csdn.net/qq_37064135/article/details/79952569
题意:给你多个case,每个case是一个n*m的矩阵,每个单原是‘W’'I''N'三种字母。可以以‘WIN'顺序(垂直和水平和L形,两边相连'W''N',中间是‘I’)相连着最多有多少个。
思路:典型网络流的最大流,难点在于建图和输入(每个矩阵间相隔一个空格)。建图:一个超级源点容量为1连接每个W,然后每个W容量为1连接每个I,每个I容量为1连接每个N,最后加入一个汇点。因为每个点只能用一次,可以把‘I’(‘W’‘N‘都可以)拆成两个点(一个入点,一个出点)。
代码:
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <cstdlib>
#include <map>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
const int maxn=5000;
const int inf=1000000000;
int dir[4][2] = {{1, 0},{-1, 0},{0, 1},{0, -1}};//上下方向
struct Edge//最大流模板
{
int from, to, cap, flow;
Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f) {}
};
struct EdmondsKarp
{
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
int a[maxn];
int p[maxn];
void init(int n)
{
for(int i = 0; i < n; i++)
G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap)
{
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
int Maxflow(int s, int t)
{
int flow = 0;
for(;;)
{
memset(a, 0, sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = inf;
while(!Q.empty())
{
int x = Q.front();
Q.pop();
for(int i = 0; i < G[x].size(); i++)
{
Edge &e = edges[G[x][i]];
if(!a[e.to] && e.cap > e.flow)
{
p[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);
Q.push(e.to);
}
}
if(a[t])
break;
}
if(!a[t])
break;
for(int u = t; u != s; u = edges[p[u]].from)
{
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
};
char s[50][50];
void solve(int nn)
{
EdmondsKarp edmo;
int n,m;
n=edmo.n=nn;
m=edmo.m=strlen(s[0]);
edmo.init(n);
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
if(s[i][j]=='W')//让W分成两个点
{
edmo.AddEdge(i * m + j, i * m + j + 1000, 1);//编号
edmo.AddEdge(n * m, i * m + j, 1);
for(int k = 0; k < 4; k++)
{
int x = i + dir[k][0];
int y = j + dir[k][1];
if(x >= 0 && x < n && y >= 0 && y < m&& s[x][y] == 'I')
edmo.AddEdge(i * m + j + 1000, x * m + y, 1);
}
}
else if(s[i][j]=='I')
{
edmo.AddEdge(i * m + j, i * m + j + 1000, 1);
for(int k = 0; k < 4; k++)
{
int x = i + dir[k][0];
int y = j + dir[k][1];
if(x >= 0 && x < n && y >= 0 && y < m&& s[x][y] == 'N')
{
edmo.AddEdge(i * m + j + 1000, x * m + y, 1);
edmo.AddEdge(x * m + y + 1000, n * m + 1, 1);
}
}
}
else
{
edmo.AddEdge(i * m + j, i * m + j + 1000, 1);
}
}
}
int ans = edmo.Maxflow(n * m, n * m + 1);
printf("%d\n", ans);
}
int main()
{
int nn=0;
while(gets(s[nn])!=NULL)//gets可以输入空行
{
if(strlen(s[nn])==0)
{
solve(nn);
nn=0;
}
else
{
nn++;
}
}
solve(nn);
return 0;
}
总结:1 学会了最大流 2 输入不够灵活应对