网络流例题总结
2015-08-01 21:39
417 查看
最大流相关例题
a:cap=供应量,cost=0;
b:cap=供应量,cost=花费;
c:cap=需求量,cost=0;
其实中间这条边b的流量是任意的(当然不能小于min(供应量,需求量)),只要正确的确定连接源点,汇点的边的流量就行了。
其实只需要把流入每个人的流量改为2就行啦,真是傻逼,啊啊啊!!
最后就是所有时刻到汇点的流量为k,跑一发最大流就行了,E-K居然也能跑2000个点,日了狗了。。。。。。
*那么我们不妨反过来考虑,也就是建边由:球队->比赛->汇点,而这些边的流量值为val,最后我们只需判断它是否满流即可(即 Maxflow=∑val ),由源点到比赛的边的流量为Max−r[i],也就是这支球队最多只能胜利的场数,一些细节稍微注意下就OK了!
*还是属于比较基础的题,这个题的启发就是正向建边不行的话,我们可以尝试反向去建边!还有就是这种最小化,最大值的问题。
以下为最小割相关例题
否则,说明流量还可以继续流向i点,毫无疑问他就属于起点那一部分了!
这里就学习到了一种寻找最小割下割边的方法,注意在不同模型下灵活使用
题目意思一样,但是多了一条件,就是在保证最大流的情况下,还要求割边的数量尽可能的少,转化思路非常巧妙,在求最大流的时候就顺便求出了割边的数量,方法就是把边权 val 转化为 val∗1000+1 。
大叫三声,好!!!好!!!好!!!
POJ 3436
题意:
生产1台电脑需要n个机器,每个机器对运过来的未加工好的机器加工,每个机器每小时有最大的生产电脑量,每台电脑有p个部分,且每台机器只会接受满足条件的未加工好的电脑,问一小时内最大的生产量?思路:
属于比较裸的最大流,按照条件建边即可,注意每一台机器有最大的生产量C,这个可以当做流入这台机器的最大流量,但是考虑特殊情况,我们必须去拆点才能建立正确的图,也就是每台机器拆成两个点,且这两个点之间的边的最大流量为C。E-K模板 getint cap , flow ; int EdmondsKarp(int s, int t) { int p , f ; queue<int> q; memset(flow, 0, sizeof(flow)); int ans = 0; while(true) { memset(f, 0, sizeof(f)); f[s] = INF; q.push(s); while(!q.empty()) { //BFS 找增广路 int u = q.front(); q.pop(); for(int v=1; v<=n; v++) if(!f[v] && cap[u][v]>flow[u][v]){ //找到新节点v p[v] = u; q.push(v); f[v] = min(f[u], cap[u][v]-flow[u][v]); } } if(f[t] == 0) break; //找不到,则当前流已经是最大流 for(int u=t; u != s; u = p[u]) { //从汇点往回走 flow[p[u]][u] += f[t]; //更新正向流量 flow[u][p[u]] -= f[t]; //更新反向流量 } ans += f[t]; //更新从 s 流出的流量 } return ans; }
POJ 3281
题意:
有N头牛,每头牛只能吃一份含特定食物及饮料的套餐,且每种食物和饮料只能被一头牛吃,问最大能喂多少头牛?思路:
一开始看上去以为是二分图匹配,但是明显分成两个图去匹配是会出现错误的,因为一头必须同时匹配食物和饮料。正确的解法就是网络流啦,建图思路比较巧妙,建议没太弄懂的好哈理解。但是一开始是这样建图源点-food-牛-drink-汇点,这样虽然满足每份food和drink只能给一头牛吃,但是没法解决每头牛只能吃一份的问题。如果是这样,源点-food-牛-牛-drink-汇点,将牛拆成两个点,里面的边权值全为1.用效率不是很高的Ek算法就能解决。所以最重要的建图过程。也是网络流的难点。POJ 1087
题意:
给m个需要充电的装置,每个装置必须有对应的插座类型,现在只有n种插座,且每种插座只有一个,一个插座只能对应一个装置,现在有k种适配器u v ,可以使得类型为u的插座可以转换为类型为v的插座,且适配器之间可以嵌套使用,每种类型适配器数量无限,求最多能有多少充电装置成功充电?思路:
二分匹配肯定是可以做的啦,不过需要加上k种电源适配器对应的边。网络流的做法呢,建图为源点 -> 充电装置 -> 插座 -> 汇点,因为充电装置只有一个,所以源点 -> 充电装置 边流量为1 ,而每种插座只能用一次,所以充电装置 -> 插座 边流量为1 ,而插座类型可以通过适配器等效的使用,所以由于加进适配器而增加的插座之间的边流量因为INF,但是注意一点就是插座 -> 汇点的建边,只能由那n个已有的插座向汇点建边,且流量为1。。。建图过程一定要理解。POJ 2195
题意:
给定一个矩阵,知道人的位置和家的位置,每个家只能容纳一个人,且每个人到达一个家需要花费,求容纳最多人的最小花费?思路:
比价明显的最小费用最大流,建图就是源点 -> 人 -> 家 -> 汇点,源点 -> 人,家 -> 汇点边的容量就是1,费用为0,而人 -> 家边的容量也是1,费用就是上面提到的花费了,然后跑一发最小费用流就行了!模板,get!struct edge { int to,next,cap,flow,cost; }e[M]; int uN,head ,tot; int pre ,dis ; bool vis ; void init() { memset(head,-1,sizeof(head)); tot=0; } void addedge(int u,int v,int cap,int cost) { e[tot].to=v , e[tot].cap=cap , e[tot].cost=cost , e[tot].flow=0; e[tot].next=head[u] , head[u]=tot++; e[tot].to=u , e[tot].cap=0 , e[tot].cost=-cost , e[tot].flow=0; e[tot].next=head[v] , head[v]=tot++; } bool spfa(int s,int t) { queue<int> q; for(int i=0 ; i<uN ;i++) { dis[i]=INF , vis[i]=0 , pre[i]=-1; } dis[s] = 0; vis[s] = 1; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; i!=-1 ;i = e[i].next) { int v = e[i].to; if(e[i].cap > e[i].flow && dis[v] > dis[u] + e[i].cost){ dis[v] = dis[u] + e[i].cost; pre[v] = i; if(!vis[v]) { vis[v] = 1; q.push(v); } } } } return pre[t] != -1; } int MincostMaxflow(int s,int t,int &cost) { int flow = 0; cost = 0; while(spfa(s,t)) { int Min_f = INF; for(int i = pre[t];i != -1 ;i = pre[e[i^1].to]) { if(Min_f> e[i].cap-e[i].flow) Min_f = e[i].cap-e[i].flow; } for(int i = pre[t];i != -1 ;i = pre[e[i^1].to]) { e[i].flow += Min_f; e[i^1].flow -= Min_f; cost += Min_f*e[i].cost; } flow += Min_f; } return flow; }
POJ 2516
题意:
有n个店主,m个储存库,有k种物品,已知每个储存库k种物品的库存,以及每个店主k种物品的需求,和物品-店主-存储库一一对应的花费,求满足所有店主需求的情况下最小的花费是多少?思路:
首先呢,这k种物品是独立互不影响的,所以我们单独考虑一种类型的物品,建图为源点->a储存库->b店主->c汇点。a:cap=供应量,cost=0;
b:cap=供应量,cost=花费;
c:cap=需求量,cost=0;
其实中间这条边b的流量是任意的(当然不能小于min(供应量,需求量)),只要正确的确定连接源点,汇点的边的流量就行了。
POJ 1459,HDU 4280,HDU 4292
思路:
这三个题之所以放在一起,是因为都是比较裸的最大流,而且要使用高效模板哦!HDU 4292跟POJ 3281是一样的题,只不过数据范围扩大到了800的样子,所以EK算法就会T了,附上高效模板-非递归形式的dicnic,代码量比较大,T_T!const int N=810,M=5+1e6,MOD=7+1e9; struct Node { int from,to,next; int cap; }edge[M]; int tol; int dep ;//dep为点的层次 int head ; void init() { tol=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int w)//第一条变下标必须为偶数 { edge[tol].from=u; edge[tol].to=v; edge[tol].cap=w;//注意正向边流量值 edge[tol].next=head[u]; head[u]=tol++; edge[tol].from=v; edge[tol].to=u; edge[tol].cap=0;//注意反向边的流量值 edge[tol].next=head[v]; head[v]=tol++; } int BFS(int start,int end) { int que ; int front,rear; front=rear=0; memset(dep,-1,sizeof(dep)); que[rear++]=start; dep[start]=0; while(front!=rear) { int u=que[front++]; if(front==N)front=0; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(edge[i].cap>0&&dep[v]==-1) { dep[v]=dep[u]+1; que[rear++]=v; if(rear>=N)rear=0; if(v==end)return 1; } } } return 0; } int dinic(int start,int end) { int res=0; int top; int stack ;//stack为栈,存储当前增广路 int cur ;//存储当前点的后继 while(BFS(start,end)) { memcpy(cur,head,sizeof(head)); int u=start; top=0; while(1) { if(u==end) { int min=INF; int loc; for(int i=0;i<top;i++) if(min>edge[stack[i]].cap) { min=edge[stack[i]].cap; loc=i; } for(int i=0;i<top;i++) { edge[stack[i]].cap-=min; edge[stack[i]^1].cap+=min; } res+=min; top=loc; u=edge[stack[top]].from; } for(int i=cur[u];i!=-1;cur[u]=i=edge[i].next) if(edge[i].cap!=0&&dep[u]+1==dep[edge[i].to]) break; if(cur[u]!=-1) { stack[top++]=cur[u]; u=edge[cur[u]].to; } else { if(top==0)break; dep[u]=-1; u=edge[stack[--top]].from; } } } return res; }
URAL 1774
题意:
一个理发师要给N个人服务,每个人要求只在时刻[s,t]内被服务两次,理发师同一时刻可以同时给k个人服务,问能否服务这N个人,可以的话,输出一种可行的方案。思路:
艹,真正比赛的时候遇到网络流就跪了,这个锅是我的,比赛的时候就已经想的是以每个时刻也当做一个点,由n个人到其所对应的时间去建边,但是一个人要被服务两次,也就是这个人需要去选择两个不同的时刻去接受服务,就是这个地方一直没有想清楚,最后时刻也确实一直没有静下心来好好思考这个,所以。。。背锅了。。。。其实只需要把流入每个人的流量改为2就行啦,真是傻逼,啊啊啊!!
最后就是所有时刻到汇点的流量为k,跑一发最大流就行了,E-K居然也能跑2000个点,日了狗了。。。。。。
SGU 326 Perspective
题意:
给定N个球队,已知每个球队现有的得分,还有每个球队还有多少比赛要打,还有这N个球队之间相互的比赛场数,问最终是否有可能球队1的得分最多或者并列最多?思路:
*首先去贪心一下,得到球队1最多能得到的分数Max,很自然的是我们可以把两个队之间的比赛当作点,如果球队 i 与 j 之间有val场比赛,那么val这个值可以分配给 i 或 j ,但是为了满足条件(即球队1的得分最多或者并列最多),我们必须通过合理的分配使得另外 n−1 个球队里的最大值最小化,那么如何去做呢?想啊,想啊,想啊,就。。一直想不出来。。。*那么我们不妨反过来考虑,也就是建边由:球队->比赛->汇点,而这些边的流量值为val,最后我们只需判断它是否满流即可(即 Maxflow=∑val ),由源点到比赛的边的流量为Max−r[i],也就是这支球队最多只能胜利的场数,一些细节稍微注意下就OK了!
*还是属于比较基础的题,这个题的启发就是正向建边不行的话,我们可以尝试反向去建边!还有就是这种最小化,最大值的问题。
以下为最小割相关例题
UVA 10480 Sabotage
题意:
给你一个简单连通的带权无向图,可以删除一些边使得点 S 和点 T 属于两个不相交的连通块,问最小花费为?所删除的边有哪些?思路:
首先呢,得明白最小割的概念,这样的话,容易知道这就是一个裸的最小割模型,最小割=最大流,所以只需求一下最大流就可得到最小的花费,但是如何求这些割边呢?跑完最大流后,在残余网络上,用f[i]数组表示由 S 流向 i 的流量,初始化为f[S]=INF,其它为0,那么按照寻找增广路的过程,如果最终f[i]为0,是不是就说明了他是属于终点那一部分,否则,说明流量还可以继续流向i点,毫无疑问他就属于起点那一部分了!
这里就学习到了一种寻找最小割下割边的方法,注意在不同模型下灵活使用
拓展:
ZOJ 3792 Romantic Value题目意思一样,但是多了一条件,就是在保证最大流的情况下,还要求割边的数量尽可能的少,转化思路非常巧妙,在求最大流的时候就顺便求出了割边的数量,方法就是把边权 val 转化为 val∗1000+1 。
大叫三声,好!!!好!!!好!!!
不断更新中。。。。。
相关文章推荐
- 虚拟机3中网络模式(桥接、nat、Host-only
- OPENWRT网络打印机TCP/IP共享设置教程 以703N为例
- HTTP协议
- LVQ神经网络的分类
- java网络编程基础:UDP
- (3) HTTP
- PNN神经网络预测类别的例子
- TCP协议详解(一)
- BP神经网络设计常用的基本方法和实用技术
- Android核心基础-7.Android 网络通信-6.访问Webservice
- ASP.Net部署网站后提示HTTP Error 404.2 – Not Found的解决方法
- Http协议的请求头信息与返回头信息详解
- jmeter http请求界面解释
- php中php://input、$_POST和$HTTP_RAW_POST_DATA的异同
- C#实现访问网络共享文件夹
- Android核心基础-7.Android 网络通信-5.发送GET、POST请求
- TCP的SACK选择确认选项
- http请求原理
- 【独立开发者er Cocos2d-x实战 013】Cocos2dx 网络编程实战之星座运势
- Android核心基础-7.Android 网络通信-4.获取网络XML,JSON