网络流总结(持续更新中)
来源:互联网 发布:淘宝客源码自动采集 编辑:程序博客网 时间:2024/05/21 22:57
1.我自己理解的网络流
我对网络流的理解就是,给定一个图,这个图有一种特点,都可以看成从某个点开始,我们称之为源点,一般情况下都将此点定位第一个点,即初始点,所有的点最终都会在一个点会和,我们称之为汇点,通常将此点定位为n,也就是最后一个点。既然是网络流,那么一定不会很简单,每条有向边之间都有一定的权值,称之为容量,用c(i,j)表示,而流过每条路径的称之为流量,用f(i,j)表示,目标就是求各种的流量。
2.网络流举例
这是最大网络流的一个典型例题,答案既不是8,也不是17等等除了10之外的答案,没错就是10,不妨自己想一下怎么得到的10.
答案是下面这样的
有点不容易懂吧,不着急慢慢来。
3.大神眼中的网络流
传送门:点击打开链接 点击打开链接
4.网络流相关
相必网络流已经了解的差不多了,那就要来看一下将网络流用代码实现的相关知识。
(1)最大流算法
最大流算法就是经过所有路径之后,得到的净最大流量,说起来简单,具体实现就要牵扯到增广路问题了。
两种求最大流的模板:
(1)dicnic模板
#include <iostream>#include <algorithm>#include <stdio.h>#include <string.h>using namespace std;const int oo=1e9;const int mm=111111;const int mn=999;int node,src,dest,edge;int ver[mm],flow[mm],_next[mm];int head[mn],work[mn],dis[mn],q[mn];void prepare(int _node,int _src,int _dest){ node=_node,src=_src,dest=_dest; for(int i=0;i<node;++i) head[i]=-1; edge=0;}void addedge(int u,int v,int c){ ver[edge]=v,flow[edge]=c,_next[edge]=head[u],head[u]=edge++; ver[edge]=u,flow[edge]=0,_next[edge]=head[v],head[v]=edge++;}bool Dicnic_bfs(){ int i,u,v,l,r=0; for(i=0;i<node;++i) dis[i]=-1; dis[q[r++]=src]=0; for(l=0;l<r;++l) for(i=head[u=q[l]];i>=0;i=_next[i]) if(flow[i]&&dis[v=ver[i]]<0) { dis[q[r++]=v]=dis[u]+1; if(v==dest) return 1; } return 0;}int Dicnic_dfs(int u,int exp){ if(u==dest) return exp; for(int &i=work[u],v,tmp;i>=0;i=_next[i]) if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dicnic_dfs(v,min(exp,flow[i])))>0) { flow[i]-=tmp; flow[i^1]+=tmp; return tmp; } return 0;}int Dicnic_flow(){ int i,ret=0,delta; while(Dicnic_bfs()) { for(i=0;i<node;++i) work[i]=head[i]; while(delta=Dicnic_dfs(src,oo)) ret+=delta; } return ret;}
(2)SAP模板
int Sap() //sap模板{ int res = 0; BFS(); int top = 0; memcpy(cur, head, sizeof(head)); int u = src, i; while (dep[src] < n) { if (u == des) { int temp = inf, inser = n; for (i=0; i!=top; ++i) if (temp > edge[stack[i]].cap) { temp = edge[stack[i]].cap; inser = i; } for (i=0; i!=top; ++i) { edge[stack[i]].cap -= temp; edge[stack[i]^1].cap += temp; } res += temp; top = inser; u = edge[stack[top]].frm; } if (u != des && gap[dep[u] -1] == 0) break; for (i = cur[u]; i != -1; i = edge[i].nxt) if (edge[i].cap != 0 && dep[u] == dep[edge[i].to] + 1) break; if (i != -1) { cur[u] = i; stack[top++] = i; u = edge[i].to; } else { int min = n; for (i = head[u]; i != -1; i = edge[i].nxt) { if (edge[i].cap == 0) continue; if (min > dep[edge[i].to]) { min = dep[edge[i].to]; cur[u] = i; } } --gap[dep[u]]; ++gap[dep[u] = min + 1]; if (u != src) u = edge[stack[--top]].frm; } } return res;}
(3)
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll long long#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const int inf=0x3f3f3f3f;const double eps=1e-6;const int maxn=1005;const int mod=1e9+7;int dir[4][2]={0,1,1,0,0,-1,-1,0};ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}ll lcm(ll a,ll b){return a/gcd(a,b)*b;}ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}int map[maxn][maxn],flow[maxn][maxn],a[maxn],path[maxn];int n,m,start,END;int maxflow(){int i,j,k;queue <int> q;mset(flow,0);int max_flow=0;while(1){mset(a,0);a[start]=inf;while(!q.empty()) q.pop();q.push(start);while(!q.empty()){int temp=q.front();q.pop();//if(temp==end)//break;for(i=0;i<n*2+1;i++){if(!a[i]&&flow[temp][i]<map[temp][i]){path[i]=temp;a[i]=min(a[temp],map[temp][i]-flow[temp][i]);q.push(i);}}}if(a[END+n]==0)break;for(j=END+n;j!=start;j=path[j]){flow[path[j]][j]+=a[END]; //反向弧相加 flow[j][path[j]]-=a[END]; //减去流量 }max_flow+=a[END+n];}return max_flow;}int main(){return 0;}
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll long long#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const ll inf=0x3f3f3f3f;const double esp=1e-6;const int maxn=10005;const int mod=1e9+7;int dir[4][2]={0,1,1,0,0,-1,-1,0};int d[maxn],start,END,n,p,k;int map[105][105],cur[maxn];int bfs(){int i,j;mset(d,-1);d[start]=0;queue <int> q;q.push(start);while(!q.empty()){int temp=q.front();q.pop();if(temp==END) //到达汇点 return 1;for(i=1;i<=n;i++){if (map[temp][i]>0&&d[i]==-1){ d[i]=d[temp]+1; q.push(i);}}}return d[END]!=-1;}int dfs(int u,int maxflow){if(u==END)return maxflow;int j,ret=0;for(int i=cur[u];i<=n;i++){int c=i;if(map[u][i]>0&&d[i]==d[u]+1&&(ret=dfs(cur[u]=i,min(maxflow,map[u][i])))){map[u][i]-=ret;map[i][u]+=ret;return ret;}}return 0;}int dinic(){int ans=0,temp;while(bfs()){for(int i=0;i<=n;i++)cur[i]=1;temp=dfs(start,inf);ans+=temp;}return ans;}int main(){int i,j,x,y,z;while(cin>>n>>p>>k){mset(map,0);for(i=0;i<p;i++){scanf("%d%d%d",&x,&y,&z);map[x][y]=z;map[y][x]=z;}start=1;END=2;int ans1=dinic();cout<<ans1<<endl;for(i=0;i<k;i++){scanf("%d%d%d",&x,&y,&z);map[x][y]+=z;map[y][x]+=z;ans1+=dinic();cout<<ans1<<endl;}}return 0;}
(2)增广路算法
算法思想其实很简单,从零流(所有边的流量均为0)开始不断增加流量,保持每次增加流量后都满足容量限制、斜对称性和流量平衡3个条件。
刘汝佳的算法竞赛入门经典中有对增广路算法的详细介绍,顺便放两条链接方便理解。
传送门:点击打开链接 点击打开链接
(3)最小割最大流定理
在一个网络流中,能够从源点到达汇点的最大流量等于,如果从网络中移除就能够导致网络流中断的边的集合的最小容量和。
参考博客:点击打开链接
最小割求最少边
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll int#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const int inf=0x3f3f3f3f;const double esp=1e-6;const int maxn=2005;const int maxm=10005;const int mod=1e9+7;int dir[4][2]={0,1,1,0,0,-1,-1,0};struct Edge{ int v,next; ll cap;}edge[maxm];int head[maxn],pre[maxn],cur[maxn],level[maxn],gap[maxn],NV,cnt,n,m;void Insert(int u,int v,ll cap){ edge[cnt].v=v;edge[cnt].cap=cap; edge[cnt].next=head[u];head[u]=cnt++; edge[cnt].v=u;edge[cnt].cap=0; edge[cnt].next=head[v];head[v]=cnt++;}ll SAP(int start,int END){ mset(pre,-1); mset(level,0); mset(gap,0); for(int i=0;i<=NV;i++)cur[i]=head[i]; int u=pre[start]=start; ll aug=-1,maxflow=0; gap[0]=NV; while(level[start]<NV){q: for(int &i=cur[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(edge[i].cap&&level[u]==level[v]+1){ aug==-1?aug=edge[i].cap:aug=min(aug,edge[i].cap); pre[v]=u; u=v; if(v==END){ maxflow+=aug; for(u=pre[u];v!=start;v=u,u=pre[u]){ edge[cur[u]].cap-=aug; edge[cur[u]^1].cap+=aug; } aug=-1; } goto q; } } int minlevel=NV; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(edge[i].cap&&minlevel>level[v]){ cur[u]=i; minlevel=level[v]; } } if(--gap[level[u]]==0)break; level[u]=minlevel+1; gap[level[u]]++; u=pre[u]; } return maxflow;}int main(){ int T,u,v,w,flag,start,END; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); scanf("%d%d",&start,&END);NV=n,cnt=0; memset(head,-1,sizeof(head)); for(int i=1;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); if(u==v) continue; Insert(u,v,(ll)w*maxm+1); } ll ans=SAP(start,END); printf("%d\n",ans%maxm); } return 0;}
(4)最小费用最大流问题
基于最小割问题,但此问题增加了访问值,即每经过一个点都要减去相应的流量值,最后求出最大流。
可以看一下这个例题:点击打开链接
参考博客:点击打开链接
模板:
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll long long#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const int inf=0x3f3f3f3f;const double eps=1e-6;const int maxn=1005;const int mod=1e9+7;int dir[4][2]={0,1,1,0,0,-1,-1,0};ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}ll lcm(ll a,ll b){return a/gcd(a,b)*b;}ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}int head[100],ep; int d[maxn],pre[maxn]; bool vis[maxn]; int q[maxn]; struct Edge { int u,v,c,w,next; }edge[maxn]; void addedge(int u,int v,int w,int c)//u v 费用 容量 { edge[ep].u=u; edge[ep].v=v; edge[ep].w=w; edge[ep].c=c; edge[ep].next=head[u]; head[u]=ep++; edge[ep].v=u; edge[ep].u=v; edge[ep].w=-w; edge[ep].c=0; edge[ep].next=head[v]; head[v]=ep++; } int SPFA(int src,int des) { int l,r; memset(pre,-1,sizeof(pre)); memset(vis,0,sizeof(vis)); for(int i=0;i<=des;i++) d[i]=inf; d[src]=0; l=0;r=0; q[r++]=src; vis[src]=1; while(l<r) { int u=q[l++]; vis[u]=0; for(int j=head[u];j!=-1;j=edge[j].next) { int v=edge[j].v; if(edge[j].c>0&&d[u]+edge[j].w<d[v]) { d[v]=d[u]+edge[j].w; pre[v]=j; if(!vis[v]) { vis[v]=1; q[r++]=v; } } } } if(d[des]==inf) return 0; return 1; } int MCMF(int src,int des) { int flow=0,ans=0;//flow是得到的最大流的值 ans得到的是最小的费用 while(SPFA(src,des)) { ans+=d[des]; int u=des; int mini=inf; while(u!=src) { if(edge[pre[u]].c<mini) mini=edge[pre[u]].c; u=edge[pre[u]].u; } flow+=mini; u=des; while(u!=src) { edge[pre[u]].c-=mini; edge[pre[u]^1].c+=mini; u=edge[pre[u]].u; } } return ans; }int main(){return 0;}
5.应用
1. UVA 10480 Sabotage (最大流)
2.
HDU 4289 Control(最大流+拆点)
3. HDU 2732 Leapin' Lizards(拆点+最大流)
4. POJ 3281 Dining(拆点+最大流)
5. BZOJ 1001 狼抓兔子
初步总结了有关网络流的算法,网络流的题类型很多,部分地方用到了邻接表,有的还会牵扯到二分、并查集、spfa等,是一类不容易掌握的题,具体题型会在整理后附上题解。 由于能力有限,博客中出错不可避免,还请各位大神多多指教。
阅读全文
1 0
- 网络流总结(持续更新中)
- 工作中总结持续更新
- 测试总结---持续更新中
- 【专题总结】网络流与二分图(持续更新)
- 网络电子书资源汇总(持续更新中......)
- 网络编程中的错误( 持续更新中)
- PHP 常用 函数总结 持续更新中...
- 内存管理总结【持续更新中.......】
- 题目总结(持续更新中)
- 经济 词汇总结 (持续更新中)
- Oracle总结(持续更新中)
- iOS错误总结。。。。。持续更新中
- Java中常见问题总结------------持续更新
- iOS常见问题总结....持续更新中
- JS兼容性问题总结(持续更新中)
- Android面试总结持续更新中
- iOS报错总结(持续更新中)
- 排序算法总结(持续更新中)
- Hiveserver2 的简单配置说明
- Android FaceDetector实现人脸检测,人脸追踪(框出人脸)(MVP模式)
- 怎样将PDF文件中的图片进行修改
- 使用openssl_encrypt方法替代mcrypt_encrypt做AES加密
- iptables学习笔记:使用NAT实现简单的无线AP
- 网络流总结(持续更新中)
- BZOJ 1202-狡猾的商人(带权并查集)
- 问题 D: 买饼干
- 微信公众平台 java 文本类型 4
- 对比excel2个sheet的数据
- dede判断手机访问电脑端网站跳转代码
- mysql主从复制和读写分离
- 四种数组排序方法 20170801
- Python日志8/2