网络流模板
/************************************************************************
网络流的定义好多呀 这里就仅仅标注定义名字吧 详细解释就交给以后的自己啦
==容量网络和网络最大流
1.容量 (容量网络,弧的容量,弧的流量)
2.网络流(可行流)—— 可行流的瞒住条件(弧流量限制条件&&平衡条件)
3.伪流
4.最大流
==链和增广路
1.饱和弧(非饱和弧)
2.零流(非零流)
3.链
4.前向弧(后向弧)
5.增广路
==残留容量和残留网络
1.残留容量(残留网络)
==割&&最小割
1.割
2.S-T割
3.割的容量(割的净流量,最小割)
K
¥¥最大流最小割定理
整体把关:
网络最大流的求解分两大类算法:1.增广路算法(包括一般增光路算法Ford-Filkerson算法 最短增光路算法 连续最短增广路算法) 2.预流推进算法(
///一般增广路算法 纯模板
#include <iostream> #include <string.h> #include <cmath> #include <stdio.h> using namespace std; #define INF 100000+10 #define Max 1000 struct Node { int c; ///容量 int f; ///流量 } mapp[Max][Max]; int pre[Max]; ///指明从哪个定点得到的 方便用到追踪的方法得到可改进量
int alphp[Max]; ///用来存放每点之间的可改进量 int flag[Max]; ///标记该点是否已经检查过了 int que[Max]; ///广搜 模拟队列 int qe,qs; ///队尾 队头 int v; ///队列顶点 int n,m; void Ford() { while(1) { ///0是起始点 memset(pre,-1,sizeof(pre)); memset(flag,-1,sizeof(flag)); memset(alphp,-1,sizeof(alphp)); flag[0]=0; pre[0]=0; alphp[0]=INF; ///之上是初始化三个数组 均标记为未使用 0点使用 0点前驱为0 0点的改进量==INF(意为0点可以流出无限多) qs=qe=0; ///队列扫描 que[qe]=0; ///the begin of que[] qe++; while(qs<qe&&flag[n-1]==-1) { v=que[qs]; ///take the top of que[] qs++; for(int i=0; i<n; i++) { if(flag[i]==-1) { ///每次在改的过程 不断更新点 边; ///正方向未满 so改进 if(mapp[v][i].c<INF&&mapp[v][i].f<mapp[v][i].c) { flag[i]=0; pre[i]=v; alphp[i]=min(alphp[v],mapp[v][i].c-mapp[v][i].f); que[qe]=i; qe++; } ///反向有流量(反向流) else if(mapp[i][v].c<INF&&mapp[i][v].f>0) { flag[i]=0; pre[i]=-v; alphp[i]=min(alphp[v],mapp[i][v].f); que[qe]=i; qe++; } } } flag[v]=1; } ///当队列为空的时候 汇点还没有被标记 或者汇点的调整量==0 则这次失败了 跳出while循环 if(flag[n-1]==-1||alphp[n-1]==0) break; ///否者就应该到追踪 更新调整了 int k1=n-1; ///最后一个点 int k2=abs(pre[k1]); ///与最后一个点连接的点(前一个点) int a=alphp[n-1]; ///可调整量 while(1) ///该路线一定是可调整路线 { if(mapp[k2][k1].f<INF) ///进行调整 在原有基础上加上调整量(更新) 正向 { mapp[k2][k1].f+=a; } else ///反向 mapp[k1][k2].f-=a; if(k2==0) ///调整到原点了 break; k1=k2; //继续向前追踪 k2=abs(pre[k2]); } } int maxFlow=0; ///==最大流 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(i==0&&mapp[i][j].f<INF) maxFlow+=mapp[i][j].f; if(mapp[i][j].f<INF) printf("%d->%d:%d\n",i,j,mapp[i][j].f); } } printf("maxFlow==%d\n",maxFlow); } int main() { // freopen("in.txt","r",stdin); int u,v,c,f; cin>>n>>m; for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { mapp[i][j].c=mapp[i][j].f=INF; ///初始化 } } for(int i=0;i<m;i++) ///建图 { cin>>u>>v>>c>>f; mapp[u][v].c=c; mapp[u][v].f=f; } Ford(); }///输入输出input:6 100 1 8 2
0 2 4 3
1 3 2 2
1 4 2 2
2 1 4 2
2 3 1 1
2 4 4 0
3 4 6 0
3 5 9 3
4 5 7 2
output:
0->1:4
0->2:4
1->3:2
1->4:2
2->1:0
2->3:1
2->4:3
3->4:0
3->5:3
4->5:5
maxFlow==8
///最短增光路算法最短增光路算法 最短增广路算法
构造残留网络—>构造层次网络——>BFS(删去不可继续增广的边,点)——>不存在增广路为止;
///连续增光路
构造残留网络——>构造层次网络——>DFS(每次DFS之后回退到起点能够到达的最远的点)——>回退到起点结束;
两者之前的部分是一样的 对应模板后续补上~~~
接下来打几道网络流的题吧~~(都是copy 但是我会详细写过程的!!!)
poj1149 poj1273 poj2112 poj1459 poj1087 poj2301 poj3436 poj1637
poj1149 一般增广路算法
仔细理解题意
重点在构造网络流
构造方法:
1. 我们将每一个猪圈看成除源点和汇点之外的点(所以我们需要添加源点和汇点)
2.源点和每个猪圈的第一个顾客相连接,边的权值是猪圈开始时猪的数目(如果源点和某个边重合了,则权值之和作为作为该边新的权值) eg:第一个猪圈的第一个顾客是1 第二个猪圈也是 所以源点和1号顾客连边的权值==4
3.顾客j紧跟着顾客i 则边<i,j>=INF ,因为卖家可以在开j之前(就是在i顾客期间 随意更换每个猪圈里猪的数目,所以j可以买到任意多的猪)
4.每个顾客和会点之间的连边是顾客希望买到猪的数目(所以汇点的流入量==顾客想买猪的总数)
![](//images0.cnblogs.com/blog/613419/201412/152012097654544.png)
第一次插图片 有些丑~~~~啦啦啦 构造的原始图就是这样的!!!!
接下来就是真正的代码啦~~
#include <string.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#define Maxm 1000
#define Maxn 10
#define INF 300000000
int s,t; ///源点 汇点
int cust[Maxn+2][Maxn+2]; ///节点之间的容量(包括源点和汇点)customer
int flow[Maxn+2][Maxn+2]; ///节点之间的流量
void init()
{
int n,m; ///猪圈数 顾客数
int num; ///每个顾客拥有的钥匙数
int house[Maxm]; ///每个猪圈中猪的数目
int last[Maxm]; ///每个猪圈的前一个顾客的序号 到追踪的翻版
int k; ///第k个猪圈的钥匙
memset(last,0,sizeof(last));
memset(house,0,sizeof(house));
cin>>n>>m;
s==0;
t=n+1; ///定义源点和汇点
for(int i=1; i<=m; i++)
cin>>house[i]; ///注意角标从1开始 0给源点了
for(int i=1; i<=n; i++)
{
cin>>num;
for(int j=0; j<num; j++)
{
cin>>k; ///钥匙的序号
if(last[k]==0) ///开通猪圈的第一把钥匙
cust[s][i]+=house[k]; ///如果有重合边 权值相加
else
cust[last[k]][i]=INF; ///如果前面有钥匙开该猪圈 则该连接边的权值无穷大
last[k]=i; ///更新其前驱~~
}
cin>>cust[i][t]; ///该顾客想买的猪的数量
}
///整个图已经构造完成
}
void Ford()
{
int pre[Maxn+2]; ///连接边的前一个定点 方便倒追踪 源点标号==-1 其他未标记的点标号初始==-2
int minflow[Maxn+2]; ///每个定点的可改进量
///广搜(必然队列模拟呀)
int que[Maxn+2];
int qs,qe; ///队头 队尾
int v; ///队列定点==当前检查的点
int p; ///可改进量 (cij-fij)
///构造零流 假设之前猪圈没有猪
memset(flow,0,sizeof(flow));
minflow[0]=INF;
///进入广搜
while(1)
{
for(int i=0; i<Maxn+2; i++)
pre[i]==-2;
pre[0]=-1; ///源点
qs=0;
que[qs]=0; ///定点qs入队
qe=1;
while(qs<qe&&pre[t]==-2) ///t是汇点
{
///当qs>=qe时 说明队列为空 标号不可以进行下去了
v=que[qs];
qs++;
for(int i=0; i<t+1; i++) ///从源点到汇点都包含在内的
{
if(pre[i]==-2&&(p=cust[v][i]-flow[v][i]))
{
pre[i]=v;
que[qe]=i;
qe++;
minflow[i]=(minflow[v]<p)?minflow[v]:p;
}
}
}
if(pre[t]==-2)
break; ///汇点没有标号 证明源点到汇点没有通路了。。。
int i,j;
for(i=pre[t],j=t; i!=-1; j=i,i=pre[i])
{
flow[i][j]+=minflow[t];
flow[j][i]-=flow[i][j];
}
for(i=0,p=0; i<t; i++)
p+=flow[i][t];
cout<<p<<endl;
}
}
int main()
{
init();
Ford();
return 0;
}