网络流(4)——最小流
来源:互联网 发布:吴彦祖 知乎 编辑:程序博客网 时间:2024/06/07 03:47
啊,学渣苦,学渣累
什么是最小流
emm…
顾名思义,最小流的前提是有下界(不需要黑曜石)
如何构建最小流
emm…好问题。
我们需要先构建一个无源汇可行流
emm…
我们先这样定义一个网络图:
推导:首先被满足的是一个等式——流量平衡条件:
以及如何表示
代入流量平衡条件,有:
移项,得:
我们现在引进一个新变量
很显然你的
当
当
所以如果对于任意原图节点
那么说明在有下界限制条件下每个节点均能满足流量守恒,则
然后你还可以判一判这个网络究竟有没有满足下界条件的可行流
(虽然很多OI题并没有打算让你干这个)
好所以我们现在来考虑如何求最小流
如果我们在加了附加源汇的图中,连一条
然后你也可以通过二分附加边的权值来确定这个图的最大可行流
什么?最小流怎么办?当然是二分上界辣
这个的复杂度是要带
所以我们考虑可行流的流量显然是边
那么接下来怎么做呢?
如果是求最大流你顺着
如果是最小流该怎么办?
对于最小流,考虑可行流可能多流了
什么?直接退流可能会使流不满足下界?
我们将第一次跑流时超级源点和超级汇点连出的边及其反向边都删掉,并且再删掉
当然是很水的例题
(1)BZOJ2502 清理雪道
滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。
有向图求最小路径数,可以覆盖所有边至少一次
直接套用上述做法即可
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>using namespace std;inline int read(){ int i=0,f=1; char ch; for(ch=getchar();!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) i=(i<<3)+(i<<1)+(ch^48); return i*f;}int buf[1024];inline void write(int x){ if(!x){putchar('0');return ;} if(x<0){putchar('-');x=-x;} while(x){buf[++buf[0]]=x%10,x/=10;} while(buf[0]) putchar(buf[buf[0]--]+48); return ;}#define stan 111#define sten 1111111int ans,S,T,n,m,k,s,t,x,y,tot1,tot2;int tot,first[sten],nxt[sten],goal[sten],wide[sten],M[stan];int level[sten];inline void addedge(int a,int b,int c){ nxt[++tot]=first[a];first[a]=tot;goal[tot]=b;wide[tot]=c; nxt[++tot]=first[b];first[b]=tot;goal[tot]=a;wide[tot]=0; return ;}bool bfs(){ static int que[sten]; int q=1; for(int i=0;i<=n+3;++i) level[i]=-1; level[S]=1;que[q]=S; for(int i=1;i<=q;++i){ int u=que[i]; for(int p=first[u];p;p=nxt[p]) if(wide[p]&&level[goal[p]]==-1){ level[goal[p]]=level[u]+1; que[++q]=goal[p]; if(goal[p]==T) return true; } } return false;}int dfs(int u,int flow){ if(u==T) return flow; int ret=0,data; for(int p=first[u];p;p=nxt[p]) if(wide[p]&&level[goal[p]]==level[u]+1){ data=dfs(goal[p],min(flow-ret,wide[p])); if(data){ wide[p]-=data; wide[p^1]+=data; ret+=data; if(ret==flow) return ret; } } level[u]=-1; return ret;}int solve(){ int ret=0; while(bfs()) ret+=dfs(S,999999999); return ret;}signed main(){ n=read(); s=0;t=n+1; S=n+2;T=n+3;tot=1; for(int i=1;i<=n;++i){ x=read(); addedge(s,i,999999999); addedge(i,t,999999999); for(int j=1;j<=x;++j){ y=read(); addedge(i,y,999999999-1); --M[i];++M[y]; } } for(int i=1;i<=n;++i){ if(M[i]>0) addedge(S,i,M[i]); if(M[i]<0) addedge(i,T,-M[i]); } addedge(t,s,999999999); solve(); for(int p=first[S];p;p=nxt[p]) wide[p]=wide[p^1]=0; for(int p=first[T];p;p=nxt[p]) wide[p]=wide[p^1]=0; ans=wide[tot]; wide[tot-1]=wide[tot]=0; S=t;T=s; ans-=solve(); write(ans); return 0;}
(2)BZOJ1458 士兵占领
有一个M * N的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第i行至少放置了Li个士兵, 第j列至少放置了Cj个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。
注意有的边下界是0
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>using namespace std;inline int read(){ int i=0,f=1; char ch; for(ch=getchar();!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) i=(i<<3)+(i<<1)+(ch^48); return i*f;}int buf[1024];inline void write(int x){ if(!x){putchar('0');return ;} if(x<0){putchar('-');x=-x;} while(x){buf[++buf[0]]=x%10,x/=10;} while(buf[0]) putchar(buf[buf[0]--]+48); return ;}#define stan 222#define sten 1111111int ans,S,T,n,m,k,s,t,x,y,tot1,tot2;int tot,first[sten],nxt[sten],goal[sten],wide[sten];int level[sten];bool ac[stan][stan];inline void addedge(int a,int b,int c){ nxt[++tot]=first[a];first[a]=tot;goal[tot]=b;wide[tot]=c; nxt[++tot]=first[b];first[b]=tot;goal[tot]=a;wide[tot]=0; return ;}bool bfs(){ static int que[sten]; int q=1; for(int i=0;i<=T;++i) level[i]=-1; level[S]=1;que[q]=S; for(int i=1;i<=q;++i){ int u=que[i]; for(int p=first[u];p;p=nxt[p]) if(wide[p]&&level[goal[p]]==-1){ level[goal[p]]=level[u]+1; que[++q]=goal[p]; if(goal[p]==T) return true; } } return false;}int dfs(int u,int flow){ if(u==T) return flow; int ret=0,data; for(int p=first[u];p;p=nxt[p]) if(wide[p]&&level[goal[p]]==level[u]+1){ data=dfs(goal[p],min(flow-ret,wide[p])); if(data){ wide[p]-=data; wide[p^1]+=data; ret+=data; if(ret==flow) return ret; } } level[u]=-1; return ret;}int solve(){ int ret=0; while(bfs()) ret+=dfs(S,999999999); return ret;}signed main(){ m=read();n=read();k=read(); S=n+m+2;T=n+m+3;tot=1; s=0;t=n+m+1; for(int i=1;i<=m;++i){ x=read(); addedge(s,i,999999999); addedge(S,i,x); tot1+=x; } addedge(s,T,tot1); for(int i=1;i<=n;++i){ x=read(); addedge(m+i,t,999999999); addedge(m+i,T,999999999); tot2+=x; } addedge(t,S,tot2); for(int i=1;i<=k;++i){ x=read();y=read(); ac[x][y]=true; } for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) if(!ac[i][j]) addedge(i,j+m,1); addedge(S,t,tot2); ans=solve(); if(ans<max(tot1,tot2)){ puts("JIONG"); return 0; }else{ for(int p=first[S];p;p=nxt[p]) wide[p]=wide[p^1]=0; for(int p=first[T];p;p=nxt[p]) wide[p]=wide[p^1]=0; S=t;T=s; ans-=solve(); write(ans); return 0; } return 0;}
- 网络流(4)——最小流
- 最大网络最小流
- 最小费用网络流
- 网络流 最小割
- 最小网络最大流
- 网络流 最小割
- 网络流之——最小费用最大流
- 网络流的征程——最小费用最大流
- 网络流(1)——最大流、最小割
- 网络流24题——最小路径覆盖问题
- 网络流一般建模方式——最小割篇
- 网络流——最小割求最大权闭合子图
- 网络流(3)——最小割树
- hdu1569网络流最小割
- 【网络流】最小路径覆盖
- 最小费用网络流,不含重边
- vijosP1524网络流最小割
- 有向强连通和网络流大讲堂——史无前例最大流(最小割)、最小费用最大流
- ubuntu16.04搜狗输入法不能打出汉字的问题
- Linux常用基本命令
- 什么是网关,网关的作用
- spring各jar包作用于spring常用注解
- Cg Programming/Unity/Lighting Textured Surfaces光照纹理表面
- 网络流(4)——最小流
- c++学习记录1
- Mybatis框架对log4j日志的支持
- 编写第一个 Hibernate 例子
- leetcode---median-of-two-sorted-arrays---链表
- java 计算过去的时间到今天的时间段
- codeforces883I 2017-2018 ACM-ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rule
- [LeetCode] 32. Longest Valid Parentheses
- RGB颜色值存储为无符号整数(包括还原RGB)【附完整源码】