网络流知识总结
算法:
1、 最大流--EdmondsKarp:BFS求增广路并记录最小残量及路径+逆路径增广,直到再无增广路。
2、 最大流--Dinic:BFS求层次图(d数组)+DFS顺层次图增广(尽可能增广),直到再无增广路。
3、 最小割—最大流算法:在求解完最大流算法之后,最小割中的边即边的两个顶点一个有标记一个无标记(满载边?)。
4、 最小费用最大流—MCMF:用BellmanFord求解cost的最短路并在最短路上进行增广,直到再无增广路。
例题:
白书:
例题11-7 UNIX插头:求最少剩几个不匹配的设备。利用floyd算法求解传递闭包以判断某设备是否可以和某插座相连+二分图的最大基数匹配。
例题11-8矩阵解压:求一种满足限制的可行解。转化为无下界+以元素大小为容量限制构二分图+最大流。强调:可行解。此种解法我认为可视作贪心。
例题11-9海军上将:有向图上S->T的两条不重合的最小权路径。不重合->结点容量为1,最小权路径->最小费用流(flow_limitted:2)。
例题11-10最优巴士路线设计:求若干个圈使每个点在圈内且权和尽量小。在圈内->每个结点只有一个前继与一个后继。一个结点拆分为二构二分图+求解其最佳完美匹配。(最佳->最小权和,完美->每个点都在某一个圈内)
紫书:
例题30 网络扩容:是否存在流量为C的流否则是否可恰好修改一边使得这样的流存在。最大流+修改最小割中的边并查看最大流。优化:1、添加flow_limitted限制流量 2、在第一次最大流的基础上修改并增广(***)注意:1、因为不止一次最大流,编写时提前的reduce(对e,e.cap -= e.flow)与每次修改时g的flow清理必要 2、最小割的判定,不能在reduce之后以cap==0判断,因为还有反向边cap==0,在模板中直接添加返回vector的Mincut。
例题31 运送超级计算机 :用最短时间自S将k台超级计算机运送到T。对于一个时间time构图求解最大流看是否可以成功运送k台计算机。构图:将一个结点拆分为time个,利用原图中的联通方式+时间连边。优化:二分答案T每次重新构图->顺序枚举T每次加一层结点。
例题32 足球联赛:根据原有胜利与球队间需要比赛的场数判断哪几个球队可以成为冠军。依次判断+贪心+公平分配。
例题33 收集者的难题。求Bob可以换得的最大贴纸种类数。构图思想:Bob可以通过一些朋友将手中的一些贴纸换作其他的一些贴纸。建点:其他人、物品种类。连边方式:根据换与不换连边。
例题35 生产销售规划。求得最大利润。以每个月为结点并拆分为二构图+不固定流量的最小费用路(这里意思是不必求解最大流量而只要求解最小费用,对应到题中就是只要盈利不需要销售数量一定多)
模板:
最大流——dinic
#include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; const int maxn = 1000+10; const int INF = 1e9; struct Edge{ int u,v,cap,flow; }; struct Dinic { int n,m,s,t; bool vis[maxn]; int d[maxn],cur[maxn]; vector<int> G[maxn]; vector<Edge> es; void init(int n) { this->n=n; es.clear(); for(int i=0;i<n;i++) G[i].clear(); } void AddEdge(int u,int v,int cap) { es.push_back((Edge){u,v,cap,0}); es.push_back((Edge){v,u,0,0}); m=es.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool BFS() { queue<int> q; memset(vis,0,sizeof(vis)); q.push(s); vis[s]=1; d[s]=0; while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<G[u].size();i++) { Edge& e=es[G[u][i]]; int v=e.v; if(!vis[v] && e.cap>e.flow) { vis[v]=1; d[v]=d[u]+1; q.push(v); } } } return vis[t]; } int DFS(int u,int a) { if(u==t || a==0) return a; int flow=0,f; for(int& i=cur[u];i<G[u].size();i++){ Edge& e=es[G[u][i]]; int v=e.v; if( d[v]==d[u]+1 && (f=DFS(v,min(a,e.cap-e.flow)))>0 ) { e.flow+=f; es[G[u][i]^1].flow-=f; flow+=f,a-=f; if(!a) break; } } return flow; } int Maxflow(int s,int t) { this->s=s , this->t=t; int flow=0; while(BFS()) { memset(cur,0,sizeof(cur)); flow+=DFS(s,INF); } return flow; } };
最小费用最大流:
#include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; const int maxn = 1000+10; const int INF = 1e9; struct Edge{ int u,v,cap,flow,cost; }; struct MCMF { int n,m,s,t; int inq[maxn],a[maxn],d[maxn],p[maxn]; vector<int> G[maxn]; vector<Edge> es; void init(int n) { this->n=n; es.clear(); for(int i=0;i<n;i++) G[i].clear(); } void AddEdge(int u,int v,int cap,int cost) { es.push_back((Edge){u,v,cap,0,cost}); es.push_back((Edge){v,u,0,0,-cost}); m=es.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool SPFA(int s,int t,int& flow,int& cost) { queue<int> q; memset(inq,0,sizeof(inq)); for(int i=0;i<n;i++) d[i]=INF; d[s]=0; inq[s]=1; p[s]=0; a[s]=INF; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); inq[u]=0; for(int i=0;i<G[u].size();i++) { Edge& e=es[G[u][i]]; int v=e.v; if(e.cap>e.flow && d[v]>d[u]+e.cost) { d[v]=d[u]+e.cost; a[v]=min(a[v],e.cap-e.flow); p[v]=G[u][i]; if(!inq[v]) { inq[v]=1; q.push(v); } } } } if(d[t]==INF) return false; flow+=a[t] , cost+=a[t]*d[t]; int u=t; while(u!=s) { es[p[u]].flow += a[t]; es[p[u]^1].flow -= a[t]; u=es[p[u]].u; } return true; } void Mincost(int s,int t,int& flow,int& cost) { flow=cost=0; while(SPFA(s,t,flow,cost)) ; } };