网络(最大)流初步+二分图初步 (浅谈EK,Dinic, Hungarian method:]
Posted 15owzly1-yiylcy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络(最大)流初步+二分图初步 (浅谈EK,Dinic, Hungarian method:]相关的知识,希望对你有一定的参考价值。
本文中 N为点数,M为边数;
EK: (brute_force) ;
每次bfs暴力找到一条增广路,更新流量,代码如下 :
时间复杂度:O(NM2);
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 struct node{ 5 int next,to,len; 6 }edge[200005]; 7 8 int val[10005],head[200005],vis[10005],from[100005]; 9 int n,m,s,t,ans,inf=2147483647,cnt=1; 10 11 inline void add_edge(int u,int v,int len){ 12 edge[++cnt].to=v; 13 edge[cnt].next=head[u]; 14 edge[cnt].len=len; 15 head[u]=cnt; 16 } 17 18 inline bool bfs(){ 19 queue <int> q; 20 memset(vis,0,sizeof(vis)); 21 val[s]=inf; 22 q.push(s);vis[s]=1; 23 while(q.size()){ 24 int u=q.front(); q.pop(); 25 for(int i=head[u];i;i=edge[i].next){ 26 int v=edge[i].to; 27 if(vis[v]||!edge[i].len)continue; 28 vis[v]=1; q.push(v); 29 from[v]=i; 30 val[v]=min(val[u],edge[i].len); 31 if(v==t)return true; 32 } 33 } 34 return false; 35 } 36 37 inline void update(){ 38 int u=t; 39 while(u!=s){ 40 int p=from[u]; 41 edge[p].len-=val[t]; 42 edge[p^1].len+=val[t]; 43 u=edge[p^1].to; 44 } 45 ans+=val[t]; 46 } 47 48 int main(){ 49 int x,y,z; 50 scanf("%d %d %d %d",&n,&m,&s,&t); 51 for(int i=1;i<=m;i++){ 52 scanf("%d %d %d",&x,&y,&z); 53 add_edge(x,y,z); 54 add_edge(y,x,0); 55 } 56 while(bfs())update(); 57 printf("%d ",ans); 58 return 0; 59 }
Dinic: 显然,EK算法每次只bfs找到一条增广路径是非常睿智低效的,Dinic算法就是通过dfs 每次找到一张增广网,从而使增广速度加快;
p.s. 当前弧优化:在dfs进行增广时,有些边已经对此次的增广做出了全部的贡献(换言之,他在当前的残量网络中已经没有贡献了),所以就不必再考虑它;
代码如下:
时间复杂度:O(Dinic(N, M)) O(N*M?) ~ O(M*N2);
1 //15owzLy1 2 //Dinic.cpp 3 //2018 10 10 11:51:55 4 #include <iostream> 5 #include <cstdio> 6 #include <cstring> 7 #include <cmath> 8 #include <algorithm> 9 #include <queue> 10 #include <vector> 11 #include <map> 12 #include <set> 13 #define lson tl, mid, rt<<1 14 #define rson mid+1, tr, rt<<1|1 15 #define pb(__) push_back(__) 16 #define fr() front() 17 #define bg() begin() 18 #define it iterator 19 #define INF 2100000000 20 typedef long long ll; 21 typedef double db; 22 23 const int N = 10005, M = 100005; 24 25 //edge begin 26 struct node { 27 int next, to, len; 28 }edge[M<<1]; 29 int head[N], cnt=1; 30 //edge end 31 int n, m, s, t; 32 33 template<typename T>inline void read(T &_) { 34 _=0;bool __=0;char ___=getchar(); 35 while(___<‘0‘||___>‘9‘){__|=(___==‘-‘);___=getchar();} 36 while(___>=‘0‘&&___<=‘9‘){_=(_<<1)+(_<<3)+(___^48);___=getchar();} 37 _=__?-_:_; 38 } 39 40 template<class T>inline void get_max(T &_, T __) { _=_>__?_:__; } 41 template<class T>inline void get_min(T &_, T __) { _=_<__?_:__; } 42 template<class T>inline void Swap(T &_, T &__) { T ___=_;_=__;__=___; } 43 template<class T>inline T abs(T _) { return _>0?_:-_; } 44 45 inline void jb(int u, int v, int w) { 46 edge[++cnt].to=v; 47 edge[cnt].next=head[u]; 48 edge[cnt].len=w; 49 head[u]=cnt; 50 } 51 52 namespace Dinic { 53 int l, r, q[N], cur[N], dep[N], Max_flow; 54 inline bool bfs() { 55 memset(dep, 0, sizeof(dep)); 56 memcpy(cur, head, sizeof(head)); 57 l=r=0; q[++r]=s; dep[s]=1; 58 while(l<r) { 59 int u=q[++l]; 60 for(int i=head[u];i;i=edge[i].next) { 61 int v=edge[i].to, w=edge[i].len; 62 if(dep[v]==0&&w) dep[v]=dep[u]+1, q[++r]=v; 63 } 64 } 65 if(dep[t]) return true; 66 else return false; 67 } 68 int dfs(int u, int min) { 69 if(min==0||u==t) return min; 70 int flow=0, f; 71 for(int &i=cur[u];i;i=edge[i].next) { 72 int v=edge[i].to, w=edge[i].len; 73 if(dep[v]==dep[u]+1&&(f=dfs(v, std::min(min, w)))) { 74 flow+=f; min-=f; 75 edge[i].len-=f; 76 edge[i^1].len+=f; 77 } 78 } 79 return flow; 80 } 81 inline void Dinic() { 82 int flow; 83 while(bfs()) 84 while(flow=dfs(s, INF)) Max_flow+=flow; 85 printf("%d ", Max_flow); 86 } 87 } 88 89 int main() { 90 #ifndef ONLINE_JUDGE 91 freopen("Dinic.in","r",stdin); 92 freopen("Dinic.out","w",stdout); 93 #endif 94 int x, y, z; 95 read(n), read(m), read(s), read(t); 96 for(int i=1;i<=m;i++) { 97 read(x), read(y), read(z); 98 jb(x, y, z), jb(y, x, 0); 99 } 100 Dinic::Dinic(); 101 return 0; 102 }
二分图:顾名思义,就是可以分成两个部分的图(把点分成两边,同一侧的点只能向另一侧的点连边);
二分图最大匹配:在边中选一个子集,使得每个点最多与该边集中的一个点相连,边数最多的子集即最大匹配;
如何求???
1.匈牙利算法(Hungarian method) : (brute_force) ———— 看代码
将点分为两部分(左右两边);(u在左边,v在右边)
mat[v]=u表示当前与v匹配的点为u;
每次dfs即寻求增广路;
1 //15owzLy1 2 //Hungary.cpp 3 //2018 10 10 17:04:04 4 #include <iostream> 5 #include <cstdio> 6 #include <cstring> 7 #include <cmath> 8 #include <algorithm> 9 #include <queue> 10 #include <vector> 11 #include <map> 12 #include <set> 13 #define lson tl, mid, rt<<1 14 #define rson mid+1, tr, rt<<1|1 15 #define pb(__) push_back(__) 16 #define fr() front() 17 #define bg() begin() 18 #define it iterator 19 #define INF 2100000000 20 typedef long long ll; 21 typedef double db; 22 23 const int N = 1005, M = 1005; 24 //edge bg; 25 struct node { 26 int next, to; 27 }edge[M*N]; 28 int head[N], cnt=1; 29 //edge end; 30 int mat[N], n, m, e; 31 bool vis[N]; 32 33 template<typename T>inline void read(T &_) { 34 _=0;bool __=0;char ___=getchar(); 35 while(___<‘0‘||___>‘9‘){__|=(___==‘-‘);___=getchar();} 36 while(___>=‘0‘&&___<=‘9‘){_=(_<<1)+(_<<3)+(___^48);___=getchar();} 37 _=__?-_:_; 38 } 39 40 template<class T>inline void get_max(T &_, T __) { _=_>__?_:__; } 41 template<class T>inline void get_min(T &_, T __) { _=_<__?_:__; } 42 template<class T>inline void Swap(T &_, T &__) { T ___=_;_=__;__=___; } 43 template<class T>inline T abs(T _) { return _>0?_:-_; } 44 45 inline void jb(int u, int v) { 46 edge[++cnt].to=v; 47 edge[cnt].next=head[u]; 48 head[u]=cnt; 49 } 50 51 bool dfs(int u) { 52 vis[u]=true; 53 for(int i=head[u];i;i=edge[i].next) { 54 int v=edge[i].to; 55 if(vis[mat[v]]) continue; 56 if(mat[v]==0||dfs(mat[v])) { 57 mat[v]=u; return true; 58 } 59 } 60 return false; 61 } 62 63 inline int Hungary() { 64 int res=0; 65 for(int i=1;i<=n;i++) { 66 memset(vis, false, sizeof(vis)); 67 if(dfs(i)) res++; 68 } 69 return res; 70 } 71 72 int main() { 73 #ifndef ONLINE_JUDGE 74 freopen("Hungary.in","r",stdin); 75 freopen("Hungary.out","w",stdout); 76 #endif 77 int x, y; 78 read(n), read(m), read(e);//n, m为左右两边点的个数,e为边数 79 while(e--) { 80 read(x), read(y); 81 if(y>m) continue; 82 jb(x, y); 83 } 84 printf("%d ", Hungary()); 85 return 0; 86 }
2.与网络流有何关系????
将源点和左边的点相连,右边的点与汇点相连,流量设为1;
左右点之间的边流量设为一个大于等于1的整数;
此时求出最大流,即为最大匹配;
感性理解一下:最大匹配是在边集中选出一个子集;一个点与该子集中的边相连,与该点与源点或者汇点所连边的流量流满;
建边过程:
1 inline void jb(int u, int v, int w) { 2 edge[++cnt].to=v; 3 edge[cnt].next=head[u]; 4 edge[cnt].len=w; 5 head[u]=cnt; 6 } 7 int main() { 8 int x, y, z; 9 read(n), read(m), read(s), read(t); 10 for(int i=1;i<=m;i++) { 11 read(x), read(y), read(z); 12 jb(x, y, z), jb(y, x, 0); 13 } 14 Dinic::Dinic(); 15 return 0; 16 }
以上是关于网络(最大)流初步+二分图初步 (浅谈EK,Dinic, Hungarian method:]的主要内容,如果未能解决你的问题,请参考以下文章