网络流算法之EK
最基础的网络流算法
不停地找增广路进行增广,直到无法增广为止
时间复杂度O(VE^2)
#include
#include
#include
#include
using namespace std;
int maxdata=0x7fffffff;
int capacity[200][200],c[1000][1000];//c[i][j]保存初值,因为每次计算都会改变capacity[i][j]的值,capacity[i][j]表示残留网络的容量
int flow[200];//标记从源点到当前点实际还有多少容量可用
int pre[200];//标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
int x[1000],y[10000],len[10000];//x,y,len存储每条边的信息
int n,m,p; //n 表示边数,m表示节点数,p表示操作数
queue myqueue;
int bfs(int src,int des)
{
int i,j;
while(!myqueue.empty())//队列清空
myqueue.pop();
for (i=1;i0&&pre[i]==-1)//i不是源点,且从当前点可以到达i点,i点不在队列中,则i点入队
{
pre[i]=index;//该路径上i的前驱点为index
flow[i]=min(capacity[index][i],flow[index]);//要通过必须满足流量小于等于所有边的最小值
myqueue.push(i);
}
}
}
if (pre[des]==-1)
return -1;
else
return flow[des];
}
int maxflow(int src,int des)//src表示源点,des表示汇点
{
int increasement=0;
int sumflow=0;
while((increasement=bfs(src,des))!=-1)//有可以流到汇点的路径
{
int k=des;
while (k!=src)
{
int last=pre[k];
capacity[last][k]-=increasement;//用路径上每一条边的容量减去该路径的流量
capacity[k][last]+=increasement;//给程序反悔和改正的机会
k=last;
}
sumflow+=increasement;
}
return sumflow;
}
int main()
{
int i,j,l;
int start,end,ci;
cin>>m>>n;
memset(capacity,0,sizeof(capacity));
memset(flow,0,sizeof(flow));
for (i=1;i<=n;i++)
{
cin>>start>>end>>ci;
x[i]=start; y[i]=end; len[i]=ci;
if (start==end)
continue;
c[start][end]=capacity[start][end]+=ci;
c[end][start]=capacity[end][start]+=ci;
}
scanf("%d",&p);
for (l=1;l<=p;l++)
{
int x1,y1,z1;
for (i=1;i<=m;i++)
for (j=1;j<=m;j++)
capacity[i][j]=c[i][j];
scanf("%d%d%d",&x1,&y1,&z1);
if (x1==0)
printf("%d\n",maxflow(y1,z1));
if (x1==1)//改变编号Y1的边的容量,
{
c[x[y1]][y[y1]]=capacity[x[y1]][y[y1]]=z1;
c[y[y1]][x[y1]]=capacity[y[y1]][x[y1]]=z1;
}
}
}//如果把网络流的图看作一系列的管道,那么求最大流就相当于从源点处注水,求最多最终有多少水可以流到汇点
网络流算法之dinic(适用于二分图,二分图中O(SQRT(V)E))
O(V^2E)
Dinic算法算是Ek算法的进阶版,在寻找增广路的基础上,增加了分层图的概念。
• 什么是分层图?
• 顾名思义,根据从S到每个点的远近不同,将�图分成若干层。
• Dinic算法即在寻找增广路的基础上,要求增广路上的每个点,属于分层图上不同的层。
多路增广
• 每次不是寻找一条增广路,而是在DFS中,只要可以就递归增广下去,实际上形成了一张增广网。
当前弧优化
• 对于每一个点,都记录上一次检查到哪一条边。因为我们每次增广一定是彻底增广(即这条已经被增广过的边已经发挥出了它全部的潜力,不可能再被增广了),下一次就不必再检查它,而直接看第一个未被检查的边
// Codevs 1993
#include
#include
#include
#include
#include
using namespace std;
const int max_n = 220;
const int max_m = 220;
const int max_e = max_m * 2;
const int inf = 1e9;
int point[max_n], nxt[max_e], v[max_e], remain[max_e], tot;
int cur[max_n], deep[max_n], n, m;//deep记录分层,cur记录检查到哪一条边(当前弧优化)
inline int getnum() {
char c; int ans = 0;
while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
ans = c - '0';
while (isdigit(c = getchar())) ans = ans * 10 + c - '0';
return ans;
}
inline void addedge(int x, int y, int cap) {
tot++; nxt[tot] = point[x]; point[x] = tot; v[tot] = y; remain[tot] = cap;
tot++; nxt[tot] = point[y]; point[y] = tot; v[tot] = x; remain[tot] = 0;
}
inline bool bfs(int s, int t) //排除容量已经为0的边,进行分层
{
memset(deep, 0x7f, sizeof(deep));
for (int i = 1; i <= n; i++)
cur[i] = point[i];
deep[s] = 0;
queue q;
q.push(s);
while (!q.empty()) {
int now = q.front(); q.pop();
for (int tmp = point[now]; tmp != -1; tmp = nxt[tmp])//注意tmp!=-1,因为从0开始存储
if (deep[v[tmp]] > inf && remain[tmp])
deep[v[tmp]] = deep[now] + 1, q.push(v[tmp]);
}
return deep[t] < inf;
}
int dfs(int now, int t, int limit) //寻找增广路,实际上应该是一个增广网,limit表示当前路径的源点到现在最窄的(剩余流量最小)的边的剩余流量
{
if (!limit || now == t) return limit;
int flow = 0, f;
for (int tmp = cur[now]; tmp != -1; tmp = nxt[tmp]) {
cur[now] = tmp;
if (deep[v[tmp]] == deep[now] + 1 && (f = dfs(v[tmp], t, min(limit, remain[tmp])))) {
flow += f;
limit -= f;
remain[tmp] -= f;
remain[tmp ^ 1] += f;
if (!limit) break;
}
}
return flow;
}
inline int dinic(int s, int t) {
int ans = 0;
while (bfs(s, t))
ans += dfs(s, t, inf);
return ans;
}
int main() {
tot = -1;
memset(point, -1, sizeof(point));
memset(nxt, -1, sizeof(nxt));
m = getnum(); n = getnum();
for (int i = 1; i <= m; i++) {
int x = getnum();
int y = getnum();
int cap = getnum();
addedge(x, y, cap);
}
cout << dinic(1, n) << endl;
}
网络流算法之isap
ISAP( Improved Shortest Augmenting Path)也是基于分层思想的最大流算法。所不同的是,它省去了Dinic每次增广后需要重新构建分层图的麻烦,而是在每次增广完成后自动更新每个点的『 标号』 (也就是所在的层)
// Codevs 1993
#include
#include
#include
#include
#include
using namespace std;
const int max_n = 220;
const int max_m = 220;
const int max_e = max_m * 2;
const int inf = 1e9;
int point[max_n], nxt[max_e], v[max_e], remain[max_e], tot;
int deep[max_n], num[max_n], cur[max_n], lastedge[max_n];//deep记录分层信息,num记录每一层的点数,lastedge记录前驱节点
bool vis[max_n];
int n, m;
inline int getnum() //快速读入
{
char c; int ans = 0;
while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
ans = c - '0';
while (isdigit(c = getchar())) ans = ans * 10 + c - '0';
return ans;
}
inline void addedge(int x, int y, int cap) //处理边的信息,注意从0开始存
{
tot++; nxt[tot] = point[x]; point[x] = tot; v[tot] = y; remain[tot] = cap;
tot++; nxt[tot] = point[y]; point[y] = tot; v[tot] = x; remain[tot] = 0;
}
inline int addflow(int s, int t)
{
int ans = inf, now = t;
while (now != s) {
ans = min(ans, remain[lastedge[now]]);
now = v[lastedge[now] ^ 1];
}
now = t;
while (now != s) {
remain[lastedge[now]] -= ans;
remain[lastedge[now] ^ 1] += ans;
now = v[lastedge[now] ^ 1];//因为isap是从汇点扩展的
}
return ans;
}
inline void bfs(int t) {
for (int i = 1; i <= n; i++)
deep[i] = n;
memset(vis, 0, sizeof(vis));
queue q;
deep[t] = 0;
q.push(t); vis[t] = true;
while (!q.empty()) {
int now = q.front(); q.pop();
for (int tmp = point[now]; tmp != -1; tmp = nxt[tmp])
if (!vis[v[tmp]] && remain[tmp ^ 1]) //remain[tmp^1]求逆向边
{
vis[v[tmp]] = true;
deep[v[tmp]] = deep[now] + 1;
q.push(v[tmp]);
}
}
}
inline int isap(int s, int t) {
int ans = 0, now = s;
bfs(t);
for (int i = 1; i <= n; i++) num[deep[i]]++;
for (int i = 1; i <= n; i++) cur[i] = point[i];//cur 当前弧优化,记录当前检查到哪一条边
while (deep[s] < n) {
if (now == t) {
ans += addflow(s, t);
now = s;
}
bool has_find = false;
for (int tmp = cur[now]; tmp != -1; tmp = nxt[tmp]) {
int u = v[tmp];
if (deep[u] + 1 == deep[now] && remain[tmp]) {
has_find = true;
cur[now] = tmp;
lastedge[u] = tmp;
now = u;
break;
}
}
if (!has_find) {//没有找到增广路,对now当前点重新标号
int minn = n - 1;
for (int tmp = point[now]; tmp != -1; tmp = nxt[tmp])
if (remain[tmp])
minn = min(minn, deep[v[tmp]]); //从所以相连节点中选取编号最小的
if (!(--num[deep[now]])) break;//GAP优化,如果编号出现了断层,说明不能再增广,那么此时的答案即为最大流
num[deep[now] = minn + 1]++;
cur[now] = point[now];
if (now != s)
now = v[lastedge[now] ^ 1];
}
}
return ans;
}
int main() {
tot = -1;
memset(point, -1, sizeof(point));
memset(nxt, -1, sizeof(nxt));
m = getnum(); n = getnum();
for (int i = 1; i <= m; i++) {
int x = getnum();
int y = getnum();
int cap = getnum();
addedge(x, y, cap);
}
cout << isap(1, n) << endl;
}