网络
什么是网络?网络其实就是有向带权图。为什么要叫网络,是因为权值是容量,容量意味着可以在单位时间内经过的上限,但是可以比上限小。
形象的奖就是一堆水管,每个水管有个大小限制流量。
中间略去一堆显然的性质。
最大流问题
寻找网络G上可能的最大流量(和一个有最大流量的可行流方案),即为网络G上的最大流问题
核心:
增广路径
若P是网络中连结源点s和汇点t的一条路,我们定义路的方向是从s到t,则路上的弧有两种:
前向弧—弧的方向与路的方向一致。前向弧的全体记为P+;
后向弧—弧的方向与路的方向相反。后向弧的全体记为P-;
设F是一个可行流,P 是从s到t的一条路,若P满足下列条件:
在P+的所有前向弧(u,v)上,0≦f(u,v) < C(u,v);
在P-的所有后向弧(u,v)上,0 < f(u,v) ≦C(u,v);
则称P是关于可行流F的一条可增广路径。
这里我们有一个显然的定理:
**最大流定理
如果残留网络上找不到增广路径,则当前流为最大流;反之,如果当前流不为最大流,则一定有增广路径。**
可以度娘搜索证明方式(算法导论上也有)
那么我们现在的问题就是找增广路径
方法①
大力dfs:
using namespace std;
const int oo=1000000;
int n,m, a[3000][3000], sum=0, forward;
bool vis[3000],check=true;
void init()
{
cin>>m>>n;
memset(a,0,sizeof(a));
for (int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
a[x][y]+=z; //这是图论里经常出现的吭,表示可能出现重边。
}
}
void dfs(int k,int l) //k是顶点的编号,l是最小的增广流量
{
vis[k]=true; //dfs必须有的标记
if (k==n) //找到汇点,
{
check=true; //全局变量,标记存在增广路径
sum+=l; forward=l; //流量可以扩充l。并记录下l,在回溯时进行流量操作。
return;
}
for (int i=1;i<=n;i++)
{
if ((a[k][i]>0)&&(!vis[i])) //dfs固有的东西。
{
dfs(i,min(a[k][i],l));
if (check) //这里是dfs后,回溯的位置
{
a[k][i]-=forward; //正向减去流量
a[i][k]+=forward; //逆向(可退流边)加上这个流量
return;
}
}
}
}
int main()
{
init();
while (check) //只要还能找到可增广路,就一直找下去。
{
check=false;
memset(vis,false,sizeof(vis));
dfs(1,oo);
}
cout<<sum<<endl;
return 0;
}
方法②:dinic 算法
先bfs一遍(注意只遍历容量不为0的边),求出所有节点的层数,用level数组记录。
接着就做网络流,其基本原理就是:在现有的level基础上,只按照level递增顺序找增广路径,且有一点不同的是当找到一条s–>t的路径的时候,不是直接结束,而是从当前路径上最小层次的顶点(顶点,当前路径最大流量的边上的点,就是下图的红点或绿点K)再次寻找可行流(一定要将当前层次的可行流全部找完啊!)
整个dfs过程分为2个操作。如果p的最后一个顶点为汇点,也就是说找到了增广路,那么对p增广,注意到增广后一定有一条或多条p中的边被删除了。这时,我们使增广路径后退至p中从源点可到达的最后一个顶点。
如果p的最后一个顶点不为汇点,那么观察最后那个的顶点u 。若在层次图中存在从u连出的一条边,比如(u,v),我们就将顶点v放入路径p中,继续dfs遍历;否则,点u对之后的dfs遍历就没有用了,我们将点u以及层次图中连到u的所有边删除,并且在p中后退一个点。
Dfs过程将会不断重复这2个操作,直到从源点连出的边全部被删除为止。
整个dinic的过程就是不断地先bfs,再dfs,直到bfs不能得到汇点的层数为止。
附上软件:
#include<bits/stdc++.h>
using namespace std;
const long long oo=165430000;
bool check;
long long n,m,lazy,ans,linkk[1100],t,deep[1100],head,tail,q[1100];
struct node
{
long long n,y,v;
}e[1100000];
long long read()
{
bool flag=true;
long long num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void init()
{
m=read();n=read();
int aa,bb,cc;
t=1;
for(int i=1;i<=m;++i)
{
aa=read();bb=read();cc=read();
e[++t].n=linkk[aa];
e[t].y=bb;
e[t].v=cc;
linkk[aa]=t;
e[++t].n=linkk[bb];
e[t].y=aa;
e[t].v=0;
linkk[bb]=t;
}
}
int dfs(int x,long long lazy)
{
if(x==n)return lazy;
int nowlazy=0;
int d=0;
for(int i=linkk[x];i&&nowlazy<lazy;i=e[i].n)
{
int v=e[i].y;
if(deep[v]==deep[x]+1&&e[i].v>0)
if(d=dfs(v,min(e[i].v,lazy-nowlazy)))
{
nowlazy+=d;
e[i].v-=d;
e[i^1].v+=d;
}
}
if(nowlazy==0) deep[x]=-1;
return nowlazy;
}
void bfs()
{
memset(deep,-1,sizeof(deep));
deep[1]=0;head=tail=1;
q[1]=1;
while(head<=tail)
{
int v=q[head];
for(int i=linkk[v];i;i=e[i].n)
if(deep[e[i].y]==-1&&e[i].v>0) deep[e[i].y]=deep[v]+1,q[++tail]=e[i].y;
head++;
}
if(deep[n]!=-1) check=true;
return;
}
int main()
{
init();
check=true;
while(check)
{
int d=0;check=false;
bfs();
if(!check) break;
while(d=dfs(1,oo)) ans+=d;
}
printf("%lld",ans);
return 0;
}