网络流24题
1. 飞行员配对问题(二分图匹配模板)
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <vector> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define lson rt<<1 12 #define rson rt<<1|1 13 #define lowbit(x) (x&-x) 14 #define fode(i, a, b) for(int i=a; i>=b; i--) 15 #define foe(i, a, b) for(int i=a; i<=b; i++) 16 #define fod(i, a, b) for(int i=a; i>b; i--) 17 #define fo(i, a, b) for(int i=a; i<b; i++) 18 //#pragma warning ( disable : 4996 ) 19 20 using namespace std; 21 typedef long long LL; 22 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 23 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 24 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 25 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 26 inline int Max(int a, int b) { return a>b ? a : b; } 27 inline int Min(int a, int b) { return a>b ? b : a; } 28 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 29 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 30 const LL INF = 0x3f3f3f3f3f3f3f3f; 31 const LL mod = 998244353; 32 const double eps = 1e-8; 33 const int inf = 0x3f3f3f3f; 34 const int maxk = 1e6+5; 35 const int maxn = 1e4+10; 36 37 struct node{ 38 int to, next; 39 }e[maxn]; 40 int w, y, cnt; 41 int linjie[210], match[210]; 42 bool vis[210]; 43 44 void addedge(int u, int v) 45 { e[cnt].to=v; e[cnt].next=linjie[u]; linjie[u]=cnt++; } 46 47 void init() 48 { 49 cin >> w >> y; 50 memset(linjie, -1, sizeof(linjie)); 51 52 int x, y; 53 while(1) 54 { 55 scanf("%d %d", &x, &y); 56 if (x==-1 && y==-1) break; 57 addedge(x, y); addedge(y, x); 58 } 59 } 60 61 bool dfs(int x) 62 { 63 for(int i = linjie[x]; ~i; i=e[i].next) 64 { 65 int to = e[i].to; 66 if (!vis[to]) { 67 vis[to] = true; 68 if (match[to]==-1 || dfs(match[to])) { 69 match[to] = x; 70 match[x] = to; 71 return true; 72 } 73 } 74 } 75 return false; 76 } 77 78 int work() 79 { 80 int ans = 0; 81 memset(match, -1, sizeof(match)); 82 for ( int i = 1; i <= w; i++ ) 83 { 84 if (match[i]+1) continue; 85 memset(vis, 0, sizeof(vis)); 86 if (dfs(i)) 87 ans++; 88 } 89 return ans; 90 } 91 92 int main() 93 { 94 95 #ifndef ONLINE_JUDGE 96 freopen("input.txt", "r", stdin); 97 #endif 98 99 init(); 100 printf("%d\n", work()); 101 102 103 return 0; 104 105 }
2. 最小路径覆盖(二分图匹配求最小路径覆盖)
这里是不可交叉最小路径覆盖,最小路径覆盖就是指在一张图中选择条数最少的路径使得所有路径能覆盖全部节点。一条路径中起点入度为0,终点出度为0,因为路径不可交叉,不是起点和终点的点出度入度都为1,那么可以将每一个点拆为两个点xa, xb,这样分成一个二分图,左半边(xa)代表n个点的出度,右半边(xb)代表n个点的入度。那么如果原图中有一条有向边u->v,即左半边的u(出度)到右半边的v(入度)连一条双向边,这样跑一遍二分图最大匹配,左边没匹配到的点就是所有路径的终点(出度为0),右边没匹配到的点是所有路径的起点(入度为0),所以最小路径数目就是n-最大匹配数。
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define pii pair<int,int> 12 #define lson rt<<1 13 #define rson rt<<1|1 14 #define lowbit(x) (x&-x) 15 #define fode(i, a, b) for(int i=a; i>=b; i--) 16 #define foe(i, a, b) for(int i=a; i<=b; i++) 17 #define fod(i, a, b) for(int i=a; i>b; i--) 18 #define fo(i, a, b) for(int i=a; i<b; i++) 19 //#pragma warning ( disable : 4996 ) 20 21 using namespace std; 22 typedef long long LL; 23 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 24 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 25 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 26 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 27 inline int Max(int a, int b) { return a>b ? a : b; } 28 inline int Min(int a, int b) { return a>b ? b : a; } 29 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 30 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 31 const LL INF = 0x3f3f3f3f3f3f3f3f; 32 const LL mod = 1e9+7; 33 const double eps = 1e-8; 34 const int inf = 0x3f3f3f3f; 35 const int maxk = 1e5+5; 36 const int maxn = 2010; 37 38 struct node { 39 int to, next; 40 }e[maxk]; 41 42 int n, m, cnt; 43 int linjie[maxn], mac[maxn]; 44 bool vis[maxn]; 45 46 void addedge( int u, int v ) 47 { e[cnt].to = v; e[cnt].next = linjie[u]; linjie[u] = cnt++; } 48 49 void init() 50 { 51 cin >> n >> m; 52 cnt = 0; 53 memset(linjie, -1, sizeof(linjie)); 54 55 int x, y; 56 foe(i, 1, m) 57 { 58 scanf("%d %d", &x, &y); 59 addedge(x, y+n); addedge(y+n, x); 60 } 61 } 62 63 bool dfs(int x) 64 { 65 for ( int i = linjie[x]; ~i; i=e[i].next ) 66 { 67 int to = e[i].to; 68 if (!vis[to]) { 69 vis[to] = true; 70 if ( mac[to]==-1 || dfs(mac[to]) ) { 71 mac[to] = x; 72 mac[x] = to; 73 return true; 74 } 75 } 76 } 77 return false; 78 } 79 80 int work() 81 { 82 int ans = 0; 83 memset(mac, -1, sizeof(mac)); 84 foe(i, 1, n) 85 { 86 if (mac[i]+1) continue; 87 memset(vis, 0, sizeof(vis)); 88 if (dfs(i)) 89 ans++; 90 } 91 return ans; 92 } 93 94 int main() 95 { 96 97 #ifndef ONLINE_JUDGE 98 freopen("input.txt", "r", stdin); 99 #endif 100 101 init(); 102 printf("%d\n", n-work()); 103 104 return 0; 105 }
3. 最长不下降子序列问题(最多不相交路径)
第一问最长上升序列的长度len,用n^2的dp解决,dp[i]表示以第i个数字结尾的子序列最长长度。
第二问是询问每个数字只可以取一次,最多可以取出多少个长度为len的不下降子序列,可以转化成最长不相交路径问题,选取超级源点s汇点t,将满足条件的点用流量为1的边连起来跑网络流得出的结果就是路径条数。但因为每个数字只能取一次,但是网络流算法是不管一个点能用多少次的,所以我们将一个点x拆为两个点xa, xb,xa->xb连一条流量为1的边。这样假如原图有经过x的边u -> x ->v,就可以变成 u -> xa -> xb -> v,因为xa->xb流量为1,所有即使有多条边经过x,最终也只能有一条路能通过xa->xb。这样就根据前面dp的结果建图就行了
第三问是说a1和an能用多次的情况下,最多可以取出多少个长度为len的不下降子序列。刚才把点拆成两个是在两点间连一条容量为1的边,则如果在两点间连一条容量无限大的边,就相当于把两点和为一点。所有如果a1可以作为起点,an可以作为终点的时候,把a1和s合成一个点,an和汇点合成一个点,就相当于该点能取无限次了。
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define lson rt<<1 12 #define rson rt<<1|1 13 #define lowbit(x) (x&-x) 14 #define fode(i, a, b) for(int i=a; i>=b; i--) 15 #define foe(i, a, b) for(int i=a; i<=b; i++) 16 #define fod(i, a, b) for(int i=a; i>b; i--) 17 #define fo(i, a, b) for(int i=a; i<b; i++) 18 //#pragma warning ( disable : 4996 ) 19 20 using namespace std; 21 typedef long long LL; 22 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 23 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 24 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 25 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 26 inline int Max(int a, int b) { return a>b ? a : b; } 27 inline int Min(int a, int b) { return a>b ? b : a; } 28 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 29 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 30 const LL INF = 0x3f3f3f3f3f3f3f3f; 31 const LL mod = 998244353; 32 const double eps = 1e-8; 33 const int inf = 0x3f3f3f3f; 34 const int maxk = 1e6+5; 35 const int maxn = 5000+10; 36 37 struct edge{ 38 int to, next, flow; 39 }e[maxk]; 40 41 int n, cnt, maxl, ans; 42 int num[maxk], linjie[maxk], dis[maxk]; 43 int dp[maxk]; //dp[i]表示第i个数字结尾的最长序列长度 44 45 void addedge( int u, int v, int w ) 46 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].flow=w; linjie[u]=cnt++; } 47 48 void getDp() 49 { 50 foe(j, 1, n) 51 { 52 dp[j] = 1; 53 fo(i, 1, j) 54 if (num[i] <= num[j] && dp[i]+1>dp[j]) 55 dp[j] = dp[i]+1; 56 } 57 } 58 59 void init() 60 { 61 cin >> n; 62 foe(i, 1, n) scanf("%d", &num[i]); 63 getDp(); 64 65 maxl = -1; 66 foe(i, 1, n) 67 maxl = Max(maxl, dp[i]); 68 printf("%d\n", maxl); 69 } 70 71 bool bfs(int s, int t) 72 { 73 queue<int> q; 74 int run; 75 76 memset(dis, -1, sizeof(dis)); 77 dis[s] = 0; 78 q.push(s); 79 while(!q.empty()) 80 { 81 run = q.front(); q.pop(); 82 for( int i = linjie[run]; ~i; i=e[i].next ) 83 { 84 int to = e[i].to, w = e[i].flow; 85 if ( dis[to] < 0 && w > 0 ) 86 { 87 dis[to] = dis[run]+1; 88 q.push(to); 89 } 90 } 91 } 92 93 if (dis[t] > 0) return true; 94 else return false; 95 } 96 97 int _find(int s, int t, int low) 98 { 99 int ff = 0; 100 if (s == t) return low; 101 for ( int i = linjie[s]; ~i; i=e[i].next ) 102 { 103 int v = e[i].to, w = e[i].flow; 104 if ( w > 0 105 && dis[v] == dis[s]+1 106 && (ff = _find(v, t, Min(w, low))) ) 107 { 108 e[i].flow -= ff; 109 e[i^1].flow += ff; 110 return ff; 111 } 112 } 113 return 0; 114 } 115 116 int dinic(int s, int t) 117 { 118 int tans; 119 while(bfs(s, t)) 120 { 121 while( tans = _find(s, t, inf)) 122 ans += tans; 123 } 124 return ans; 125 } 126 127 void solve1() 128 { 129 cnt = 0; 130 memset(linjie, -1, sizeof(linjie)); 131 foe(i, 1, n) { 132 if ( dp[i] == 1 ) addedge(0, i, 1), addedge(i, 0, 1); 133 if ( dp[i] == maxl ) addedge(i+n, 2*n+1, 1), addedge(2*n+1, i+n, 1); 134 addedge(i, i+n, 1); addedge(i+n, i, 1); 135 foe(j, i+1, n) { 136 if ( dp[j] == dp[i]+1 && num[j] >= num[i] ) 137 addedge(i+n, j, 1), addedge(j, i+n, 1); 138 } 139 } 140 } 141 142 bool solve2() 143 { 144 bool flag = true; 145 foe(i, 1, n-1) if (num[i]<=num[i+1]) flag = false; 146 if (flag) return true; 147 148 149 addedge(0, 1, inf); addedge(1, 0, inf); 150 addedge(1, 1+n, inf); addedge(1+n, 1, inf); 151 if (dp[n] == maxl) 152 { 153 addedge(n+n, 2*n+1, inf); addedge(2*n+1, n+n, inf); 154 addedge(n, n+n, inf); addedge(n+n, n, inf); 155 } 156 157 return false; 158 } 159 160 int main() 161 { 162 163 #ifndef ONLINE_JUDGE 164 freopen("input.txt", "r", stdin); 165 #endif 166 167 init(); 168 solve1(); dinic(0, 2*n+1); 169 printf("%d\n", ans); 170 171 if (solve2()) 172 printf("%d\n", n); 173 else 174 dinic(0, 2*n+1), printf("%d\n", ans); 175 176 return 0; 177 178 }
4. 魔术球问题(最小路径覆盖)
给n个柱子,每次在任意一根柱子最上面按顺序放上一个球,球的编号顺序是1,2,3.....,要求柱子上相邻的球的编号加起来为完全平方数。可以用最小路劲覆盖去枚举,即:按顺序每次加入新的球编号为k,遍历i < k,如果i,k两球编号加起来为完全平方数,则两球连边(当然首先得把每个球拆成两个点化成二分图连上左部和右部),然后跑最小路径覆盖看有多少条路径ans,如果ans>柱子数目n,说明最少也需要n+1条柱子(路径)才能容纳k个球,所以答案是k-1。
另外如果每次用匈牙利算法求最大匹配重复计算太多显得很愚蠢,所以用网络流dinic计算二分图最大匹配,因为dinic算法允许加入新边并在原来的残量网络继续增广,这就需要好好理解网络流算法了。至于路径,网络流其实不好记录路径,所幸这题是二分图且各个边容量为1,所以当一条左部连向右部的边容量为0时,代表这条边肯定在路径上(一条边必定被增广了容量才会减少),然后用类似邻接表的方法存储路径就可以了。
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define pii pair<int,int> 12 #define lson rt<<1 13 #define rson rt<<1|1 14 #define lowbit(x) (x&-x) 15 #define fode(i, a, b) for(int i=a; i>=b; i--) 16 #define foe(i, a, b) for(int i=a; i<=b; i++) 17 #define fod(i, a, b) for(int i=a; i>b; i--) 18 #define fo(i, a, b) for(int i=a; i<b; i++) 19 //#pragma warning ( disable : 4996 ) 20 21 using namespace std; 22 typedef long long LL; 23 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 24 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 25 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 26 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 27 inline int Max(int a, int b) { return a>b ? a : b; } 28 inline int Min(int a, int b) { return a>b ? b : a; } 29 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 30 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 31 const LL INF = 0x3f3f3f3f3f3f3f3f; 32 const LL mod = 1e9+7; 33 const double eps = 1e-8; 34 const int inf = 0x3f3f3f3f; 35 const int maxk = 1e5+5; 36 const int maxm = 2e4+10; 37 const int maxn = 5010; 38 39 struct node { 40 int to, next, flow; 41 }e[maxk]; 42 43 int n, cnt, mm = 60; 44 int linjie[maxn], dis[maxn], to[maxn]; 45 bool is[maxm], vis[maxn]; 46 47 void addedge(int u, int v, int f) 48 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].flow=f; linjie[u]=cnt++; } 49 50 void init() 51 { 52 cnt = 0; memset(linjie, -1, sizeof(linjie)); 53 cin >> n; 54 55 for ( int i = 1; i*i < maxm; i++ ) 56 is[i*i] = true; 57 } 58 59 bool bfs(int s, int t) 60 { 61 queue<int> q; 62 int run; 63 64 memset(dis, -1, sizeof(dis)); 65 dis[s] = 0; q.push(s); 66 while(!q.empty()) 67 { 68 run = q.front(); q.pop(); 69 70 for ( int i = linjie[run]; ~i; i=e[i].next ) 71 { 72 int v = e[i].to, w = e[i].flow; 73 if ( dis[v]<0 && w > 0 ) 74 { 75 dis[v] = dis[run]+1; 76 q.push(v); 77 } 78 } 79 } 80 if (dis[t]>0) return true; 81 else return false; 82 } 83 84 int dfs(int s, int t, int low) 85 { 86 int ff = 0; 87 if (s==t) return low; 88 for ( int i = linjie[s]; ~i; i=e[i].next ) 89 { 90 int v = e[i].to, w = e[i].flow; 91 if ( w > 0 92 && dis[v] == dis[s]+1 93 && (ff = dfs(v, t, Min(w, low))) ) 94 { 95 e[i].flow -= ff; 96 e[i^1].flow += ff; 97 return ff; 98 } 99 } 100 return 0; 101 } 102 103 int dinic(int s, int t) 104 { 105 int sum = 0; 106 int tans; 107 while(bfs(s, t)) 108 { 109 while(tans = dfs(s, t, inf)) 110 sum += tans; 111 } 112 return sum; 113 } 114 115 void caculatePath(int x) 116 { 117 memset(to, -1, sizeof(to)); 118 memset(vis, 0, sizeof(vis)); 119 foe(i, 1, x) 120 { 121 for( int j = linjie[i]; ~j; j=e[j].next ) 122 { 123 ///去掉起点 124 if (!to) continue; 125 ///flow为0的边必然在路径中 126 if (!e[j].flow) 127 { to[i] = e[j].to-2000; break; } 128 } 129 } 130 } 131 132 int main() 133 { 134 135 #ifndef ONLINE_JUDGE 136 freopen("input.txt", "r", stdin); 137 #endif 138 139 int ans = 0; 140 int s = 0, t = 5000, tmp = 0; 141 init(); 142 143 144 while(1) 145 { 146 if (tmp-ans > n) break; 147 148 tmp++; 149 addedge(s, tmp, 1); addedge(tmp, s, 1); 150 addedge(tmp+2000, t, 1); addedge(t, tmp+2000, 1); 151 for( int i = 1; i < tmp; i++ ) 152 { 153 if (is[i+tmp]) 154 addedge(i, tmp+2000, 1), addedge(tmp+2000, i, 1); 155 } 156 157 ans += dinic(s, t); 158 } 159 160 caculatePath(tmp-1); 161 162 cout << tmp-1 << endl; 163 foe(i, 1, tmp-1) 164 { 165 if (vis[i]) continue; 166 167 t = i; 168 while(t+1) 169 { 170 vis[t] = true; 171 printf("%d ", t); 172 t = to[t]; 173 } 174 printf("\n"); 175 } 176 177 return 0; 178 }
5. (插一个)HihoCoder - 1393(二分图多重匹配模板)
就是用最大流求二分图流量...另外这题不用前向弧优化得2000+ms,用了可以跑进15ms...tql
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define pii pair<int,int> 12 #define lson rt<<1 13 #define rson rt<<1|1 14 #define lowbit(x) (x&-x) 15 #define fode(i, a, b) for(int i=a; i>=b; i--) 16 #define foe(i, a, b) for(int i=a; i<=b; i++) 17 #define fod(i, a, b) for(int i=a; i>b; i--) 18 #define fo(i, a, b) for(int i=a; i<b; i++) 19 //#pragma warning ( disable : 4996 ) 20 21 using namespace std; 22 typedef long long LL; 23 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 24 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 25 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 26 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 27 inline int Max(int a, int b) { return a>b ? a : b; } 28 inline int Min(int a, int b) { return a>b ? b : a; } 29 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 30 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 31 const LL INF = 0x3f3f3f3f3f3f3f3f; 32 const LL mod = 1e9+7; 33 const double eps = 1e-8; 34 const int inf = 0x3f3f3f3f; 35 const int maxk = 1e5+5; 36 const int maxm = 5e4+10; 37 const int maxn = 510; 38 39 struct node { 40 int to, next, flow; 41 }e[maxm]; 42 43 int s, t, all; 44 int n, m, cnt, para; 45 int linjie[maxn], dis[maxn], cur[maxn]; 46 int need[210], a[210], b[210]; 47 48 void addedge(int u, int v, int f) 49 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].flow=f; linjie[u]=cnt++; } 50 51 void init() 52 { 53 all = cnt = 0; 54 memset(linjie, -1, sizeof(linjie)); 55 56 cin >> n >> m; 57 foe(i, 1, m) 58 { 59 scanf("%d", &need[i]); all += need[i]; 60 addedge(i+para, t, need[i]); addedge(t, i+para, need[i]); 61 } 62 63 foe(i, 1, n) 64 { 65 scanf("%d %d", &a[i], &b[i]); 66 addedge(s, i, a[i]); addedge(i, s, a[i]); 67 68 int tmp; 69 foe(j, 1, b[i]) 70 { 71 scanf("%d", &tmp); 72 addedge(i, tmp+para, 1); addedge(tmp+para, i, 1); 73 } 74 } 75 } 76 77 bool bfs() 78 { 79 queue<int> q; 80 memset(dis, -1, sizeof(dis)); 81 82 dis[s] = 0; q.push(s); 83 while(!q.empty()) 84 { 85 int run = q.front(); q.pop(); 86 for( int i = linjie[run]; ~i; i=e[i].next ) 87 { 88 int v = e[i].to, w = e[i].flow; 89 if ( dis[v] < 0 && w > 0 ) 90 { 91 dis[v] = dis[run]+1; 92 q.push(v); 93 } 94 } 95 } 96 if (dis[t] > 0) return true; 97 else return false; 98 } 99 100 int dfs(int x, int low) 101 { 102 int ff = 0; 103 if (x == t) return low; 104 for ( int &i = cur[x]; ~i; i=e[i].next ) 105 { 106 int v = e[i].to, w = e[i].flow; 107 if ( w > 0 108 && dis[v] == dis[x]+1 109 && (ff = dfs(v, Min(w, low))) ) 110 { 111 e[i].flow -= ff; 112 e[i^1].flow += ff; 113 return ff; 114 } 115 } 116 return 0; 117 } 118 119 int dinic() 120 { 121 int ans = 0; 122 int tans; 123 while(bfs()) 124 { 125 foe(i, 0, maxn) 126 cur[i] = linjie[i]; 127 while( tans = dfs(s, inf)) 128 ans += tans; 129 } 130 return ans; 131 } 132 133 int main() 134 { 135 136 #ifndef ONLINE_JUDGE 137 freopen("input.txt", "r", stdin); 138 #endif 139 140 para = 200; 141 s = 0; t = 500; 142 int T; cin >> T; 143 while(T--) 144 { 145 init(); 146 //int tmp = dinic(); 147 if ( all == dinic() ) 148 cout << "Yes" << endl; 149 else 150 cout << "No" << endl; 151 } 152 return 0; 153 }
/圆桌问题,这个还要记录一下路径,不过也很简单(这个必须得加前向弧优化才能过,我还以为是数组开小了WA了好多次)
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define pii pair<int,int> 12 #define lson rt<<1 13 #define rson rt<<1|1 14 #define lowbit(x) (x&-x) 15 #define fode(i, a, b) for(int i=a; i>=b; i--) 16 #define foe(i, a, b) for(int i=a; i<=b; i++) 17 #define fod(i, a, b) for(int i=a; i>b; i--) 18 #define fo(i, a, b) for(int i=a; i<b; i++) 19 //#pragma warning ( disable : 4996 ) 20 21 using namespace std; 22 typedef long long LL; 23 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 24 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 25 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 26 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 27 inline int Max(int a, int b) { return a>b ? a : b; } 28 inline int Min(int a, int b) { return a>b ? b : a; } 29 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 30 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 31 const LL INF = 0x3f3f3f3f3f3f3f3f; 32 const LL mod = 1e9+7; 33 const double eps = 1e-8; 34 const int inf = 0x3f3f3f3f; 35 const int maxk = 1e5+5; 36 const int maxm = 1e6+10; 37 const int maxn = 5010; 38 39 struct node { 40 int to, next, flow; 41 }e[maxm]; 42 43 int s, t, para, all; 44 int m, n, cnt; 45 int linjie[maxn], dis[maxn], cur[maxn]; 46 vector<int> vec[300]; 47 48 void addedge( int u, int v, int w ) 49 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].flow=w; linjie[u]=cnt++; } 50 51 void init() 52 { 53 all = cnt = 0; memset(linjie, -1, sizeof(linjie)); 54 s = 0; t = 1000; para = 500; 55 cin >> m >> n; 56 57 int tmp; 58 foe(i, 1, m) scanf("%d", &tmp), addedge(s, i, tmp), addedge(i, s, 0), all += tmp; 59 foe(i, 1, n) scanf("%d", &tmp), addedge(i+para, t, tmp), addedge(t, i+para, 0); 60 foe(i, 1, m) 61 foe(j, 1, n) 62 addedge(i, j+para, 1), addedge(j+para, i, 0); 63 } 64 65 bool bfs() 66 { 67 queue<int> q; 68 memset(dis, -1, sizeof(dis)); 69 70 dis[s] = 0; q.push(s); 71 while(!q.empty()) 72 { 73 int u = q.front(); q.pop(); 74 for ( int i = linjie[u]; ~i; i=e[i].next ) 75 { 76 int v = e[i].to, w = e[i].flow; 77 if ( dis[v]<0 && w>0 ) 78 { 79 dis[v] = dis[u] + 1; 80 q.push(v); 81 } 82 } 83 } 84 85 if (dis[t]>0) return true; 86 else return false; 87 } 88 89 int dfs(int x, int low) 90 { 91 int ff = 0; 92 if (x == t) return low; 93 94 for ( int &i = cur[x]; ~i; i=e[i].next ) 95 { 96 int v = e[i].to, w = e[i].flow; 97 if ( w > 0 98 && dis[v] == dis[x] + 1 99 && (ff = dfs(v, Min(w, low))) ) 100 { 101 e[i].flow -= ff; 102 e[i^1].flow += ff; 103 return ff; 104 } 105 } 106 return 0; 107 } 108 109 int dinic() 110 { 111 int ans = 0; 112 int tans; 113 while(bfs()) 114 { 115 foe(i, 0, t) cur[i] = linjie[i]; 116 while( tans = dfs(s, inf) ) 117 ans += tans; 118 } 119 return ans; 120 } 121 122 void workPath() 123 { 124 foe(i, 1, m) 125 { 126 for( int j = linjie[i]; ~j; j=e[j].next ) 127 { 128 int v = e[j].to, w = e[j].flow; 129 if (!v) continue; ///去掉起点 130 if (!w) vec[i].push_back(v-para); 131 } 132 } 133 } 134 135 136 int main() 137 { 138 139 #ifndef ONLINE_JUDGE 140 freopen("input.txt", "r", stdin); 141 #endif 142 143 init(); 144 //cout << dinic() << endl; 145 if ( all == dinic() ) { 146 printf("1\n"); 147 workPath(); 148 foe(i, 1, m) 149 { 150 fo(j, 0, (int)vec[i].size()) 151 printf("%d ", vec[i][j]); 152 printf("\n"); 153 } 154 } else { 155 printf("0\n"); 156 } 157 158 return 0; 159 }
6. HihoCoder - 1378(最小割最大流)
将一张网络的所有点分成两部分,一半包含源点S设为G1,一半包含汇点T设为G2,我们将这种划分称为网络的割(显然一个网络可能有很多种割)。定义边集{E|(u, v),u,v不同时属于G1或G2}(即连接G1和G2的边)。
因为E中的每条边都有其流量和容量,设F为从G1流向G2的流量,称为割的流量,易知只要将E中的边流量相加求和就行了(如果某条边(u, v)是从G2流到G1则要变成加相反数);设C为从G1流向G2的边容量之和,称为割的容量,这里我们要求边(u,v)必须是从G1指向G2才能将容量相加,所以F可能为负数(所有边都是从G2指向G1时 F = -Σf(u, v)),而C一定为非负数(当所有边都是从G2指向G1时C = 0)。
因为每条边的流量必定小于等于容量,所以很容易知道F <= C, 并且我们可以证得一个网络中任意一个割的F就等于当前网络的流量(从S流到T的量)。最大流最小割定理就是:一个网络的最大流流量maxflow一定等于某个割的C,又因为割的流量Fi等于网络流量,即maxflow = C = F;直观上来理解,网络流量一定等于某个割的F,而每个割的F又一定小于等于该割的C,所以该网络中所有的割的C中最小的那一个C,就是网络流所能达到的最大流量F,至于证明就不太懂了。
在这里说下怎么把G1分出来,先跑一遍dinic,这样剩下的图就是残余网络了,这个时候从源点开始dfs/bfs,当一条边流量w > 0 的时候,说明这条边流量并没有满,所以这条边两个端点必然都在G1中;如果一条边(u, v)流量w = 0的时候,说明这条路流量满了,即这条路流量等于其容量,说明这条边必定属于边集E,所以此时u在G1中,v在G2中
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <set> 6 #include <stack> 7 #include <math.h> 8 #include <string> 9 #include <algorithm> 10 11 #define SIGMA_SIZE 26 12 #define pii pair<int,int> 13 #define lson rt<<1 14 #define rson rt<<1|1 15 #define lowbit(x) (x&-x) 16 #define fode(i, a, b) for(int i=a; i>=b; i--) 17 #define foe(i, a, b) for(int i=a; i<=b; i++) 18 #define fod(i, a, b) for(int i=a; i>b; i--) 19 #define fo(i, a, b) for(int i=a; i<b; i++) 20 //#pragma warning ( disable : 4996 ) 21 22 using namespace std; 23 typedef long long LL; 24 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 25 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 26 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 27 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 28 inline int Max(int a, int b) { return a>b ? a : b; } 29 inline int Min(int a, int b) { return a>b ? b : a; } 30 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 31 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 32 const LL INF = 0x3f3f3f3f3f3f3f3f; 33 const LL mod = 1e9+7; 34 const double eps = 1e-8; 35 const int inf = 0x3f3f3f3f; 36 const int maxk = 1e5+5; 37 const int maxm = 1e6+10; 38 const int maxn = 1010; 39 40 struct edge { 41 int to, next, flow; 42 }e[maxm]; 43 44 int n, m, cnt, siz, g[502][502]; 45 int linjie[maxn], dis[maxn], cur[maxn]; 46 bool vis[maxn], chos[maxn]; 47 48 void addedge(int u, int v, int w) 49 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].flow=w; linjie[u]=cnt++; } 50 51 bool bfs(int s, int t) 52 { 53 queue<int> q; 54 memset(dis, -1, sizeof(dis)); 55 56 dis[s] = 0; q.push(s); 57 while(!q.empty()) 58 { 59 int run = q.front(); q.pop(); 60 for ( int i = linjie[run]; ~i; i=e[i].next ) 61 { 62 int v = e[i].to, w = e[i].flow; 63 if ( dis[v]<0 && w>0 ) 64 { 65 dis[v] = dis[run]+1; 66 q.push(v); 67 } 68 } 69 } 70 71 if (dis[t]>0) return true; 72 else return false; 73 } 74 75 int dfs(int x, int t, int low) 76 { 77 int ff = 0; 78 if ( x==t ) return low; 79 80 for ( int &i = cur[x]; ~i; i=e[i].next ) 81 { 82 int v = e[i].to, w = e[i].flow; 83 if ( w > 0 84 && dis[v] == dis[x]+1 85 && (ff = dfs(v, t, Min(w, low))) ) 86 { 87 e[i].flow -= ff; 88 e[i^1].flow += ff; 89 return ff; 90 } 91 } 92 return 0; 93 } 94 95 int dinic(int s, int t) 96 { 97 int tans, ans = 0; 98 while(bfs(s, t)) 99 { 100 foe(i, 1, n) cur[i] = linjie[i]; 101 while( tans = dfs(s, t, inf) ) 102 ans += tans; 103 } 104 return ans; 105 } 106 107 void init() 108 { 109 cnt = 0; memset(linjie, -1, sizeof(linjie)); 110 cin >> n >> m; 111 112 int x, y, w; 113 foe(i, 1, m) scanf("%d%d%d", &x, &y, &w), g[x][y] += w; 114 foe(i, 1, n) 115 foe(j, 1, n) 116 if (g[i][j]) 117 addedge(i, j, g[i][j]), addedge(j, i, 0); 118 } 119 120 void _find(int x) 121 { 122 chos[x] = vis[x] = true; 123 for ( int i = linjie[x]; ~i; i=e[i].next ) 124 { 125 int v = e[i].to, w = e[i].flow; 126 if ( w > 0 ) chos[v] = true; 127 if ( vis[v] || w==0 ) continue; 128 _find(v); 129 } 130 } 131 132 int main() 133 { 134 135 #ifndef ONLINE_JUDGE 136 freopen("input.txt", "r", stdin); 137 #endif 138 139 init(); 140 printf("%d ", dinic(1, n)); 141 _find(1); 142 143 int siz = 0; 144 foe(i, 1, n) if (chos[i]) siz++; 145 printf("%d\n", siz); 146 147 foe(i, 1, n) if (chos[i]) printf("%d ", i); 148 cout << endl; 149 150 cout << endl; 151 return 0; 152 }
7. P2774 (二分图最小点权覆盖/二分图最大点权独立集)
一个n*m的方格,每个方格一个权值,问相邻的方格不能同时选时可以选择出最大权值,如果将相邻方格之间连一根线就能变成求最大点权独立集,那么要转换成二分图,根据题意可以把方格分为黑白了两种颜色,每个黑方块向它相邻白方块连一根线,然后根据二分图最大点权独立集 = 点权和 - 二分图最小点权覆盖 = 点权和 - 最小割 = 点权和 - 最大流。从源点s向黑方块连线,白方块向汇点t连线,因为我们要求的最小割一定和点权有关,和汇点源点之间边的权值就是方块的权值,相当于把点权变为边权,而方块之间不应该有流量限制(不应作为割的边集),所以它们之间流量为inf。然后跑网络流就行了。
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define lson rt<<1 12 #define rson rt<<1|1 13 #define lowbit(x) (x&-x) 14 #define fode(i, a, b) for(int i=a; i>=b; i--) 15 #define foe(i, a, b) for(int i=a; i<=b; i++) 16 #define fod(i, a, b) for(int i=a; i>b; i--) 17 #define fo(i, a, b) for(int i=a; i<b; i++) 18 //#pragma warning ( disable : 4996 ) 19 20 using namespace std; 21 typedef long long LL; 22 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 23 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 24 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 25 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 26 inline int Max(int a, int b) { return a>b ? a : b; } 27 inline int Min(int a, int b) { return a>b ? b : a; } 28 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 29 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 30 const LL INF = 0x3f3f3f3f3f3f3f3f; 31 const LL mod = 1e9+7; 32 const double eps = 1e-8; 33 const int inf = 0x3f3f3f3f; 34 const int maxm = 10100; 35 const int maxn = 105; 36 37 struct edge{ 38 int to, next, val; 39 }e[100010]; 40 41 int g[maxn][maxn], linjie[maxm]; 42 int all, row, col, cnt, st, ed, dis[maxm]; 43 bool vis[maxn][maxn]; 44 45 int getid(int i, int j) 46 { return (i-1)*col + j; } 47 48 void addedge(int u, int v, int w) 49 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].val=w; linjie[u]=cnt++; } 50 51 void init() 52 { 53 cnt = all = 0; 54 cin >> row >> col; 55 foe(i, 1, row) foe(j, 1, col) scanf("%d", &g[i][j]), all += g[i][j]; 56 57 vis[1][1] = true; 58 foe(i, 2, row) vis[i][1] = !vis[i-1][1]; 59 foe(i, 1, row) foe(j, 2, col) vis[i][j] = !vis[i][j-1]; 60 } 61 62 void makeMap() 63 { 64 memset(linjie, -1, sizeof(linjie)); 65 foe(i, 1, row) 66 { 67 foe(j, 1, col) 68 { 69 if (vis[i][j]) { 70 addedge(st, getid(i,j), g[i][j]), addedge(getid(i,j), st, 0); 71 if (i > 1) addedge(getid(i,j), getid(i-1,j), inf), addedge(getid(i-1,j), getid(i,j), 0); 72 if (i < row) addedge(getid(i,j), getid(i+1,j), inf), addedge(getid(i+1,j), getid(i,j), 0); 73 if (j > 1) addedge(getid(i,j), getid(i,j-1), inf), addedge(getid(i,j-1), getid(i,j), 0); 74 if (j < col) addedge(getid(i,j), getid(i,j+1), inf), addedge(getid(i,j+1), getid(i,j), 0); 75 } 76 else { 77 addedge(getid(i,j), ed, g[i][j]), addedge(ed, getid(i,j), 0); 78 } 79 } 80 } 81 } 82 83 bool bfs(int s, int t) 84 { 85 queue<int> q; memset(dis, -1, sizeof(dis)); 86 q.push(s); dis[s] = 0; 87 while(!q.empty()) 88 { 89 int u = q.front(); q.pop(); 90 for ( int i = linjie[u]; ~i; i=e[i].next ) 91 { 92 int v = e[i].to, w = e[i].val; 93 if ( dis[v]<0 && w>0 ) 94 { 95 q.push(v); 96 dis[v] = dis[u]+1; 97 } 98 } 99 } 100 101 if (dis[t] > 0) return true; 102 else return false; 103 } 104 105 int dfs(int s, int t, int low) 106 { 107 int ff = 0; 108 if (s == t) return low; 109 110 for ( int i = linjie[s]; ~i; i=e[i].next ) 111 { 112 int v = e[i].to, w = e[i].val; 113 if ( w>0 114 && dis[v]==dis[s]+1 115 && (ff = dfs(v, t, Min(low, w))) ) 116 { 117 e[i].val -= ff; 118 e[i^1].val += ff; 119 return ff; 120 } 121 } 122 return 0; 123 } 124 125 int dinic(int s, int t) 126 { 127 int tans, ans = 0; 128 while(bfs(s, t)) 129 { 130 while(tans = dfs(s, t, inf)) 131 ans += tans; 132 } 133 return ans; 134 } 135 136 137 138 int main() 139 { 140 141 #ifndef ONLINE_JUDGE 142 freopen("input.txt", "r", stdin); 143 #endif 144 145 st = 0, ed = 10005; 146 init(); 147 makeMap(); 148 149 cout << all - dinic(st, ed) << endl; 150 }
8. POJ 2135(费用流)
费用流其实就是在网络流基础的改进,网络流算法中选择路径增广是dfs寻找增广路,可以说是随机寻找增广路的,但是如果要使费用最小显然不能随便增广,如果我们每次都选择一条费用最小的可增广路,即这条路要满足两个条件:可增广, 费用最小。我们把费用看作两条路之间的距离,那跑一遍最短路就能找到费用最短的路了,但是这条路不一定能增广,所以比平常最短路判断条件要加一个flow > 0 罢了;因为dijkstra不能用于有负权的路(建图的时候反向边的费用自然是负的),而如果最小费用存在肯定是不存在负环的,所以正符合spfa的要求,英雌用spfa找最短路,找到这条最短路并对其路径进行记录,然后选择这条路上流量最小的flow增广即可。
1 #include <iostream> 2 #include <string.h> 3 #include <cstdio> 4 #include <queue> 5 #include <stack> 6 #include <math.h> 7 #include <string> 8 #include <algorithm> 9 10 #define SIGMA_SIZE 26 11 #define lson rt<<1 12 #define rson rt<<1|1 13 #define lowbit(x) (x&-x) 14 #define fode(i, a, b) for(int i=a; i>=b; i--) 15 #define foe(i, a, b) for(int i=a; i<=b; i++) 16 #define fod(i, a, b) for(int i=a; i>b; i--) 17 #define fo(i, a, b) for(int i=a; i<b; i++) 18 //#pragma warning ( disable : 4996 ) 19 20 using namespace std; 21 typedef long long LL; 22 inline LL LMax(LL a, LL b) { return a>b ? a : b; } 23 inline LL LMin(LL a, LL b) { return a>b ? b : a; } 24 inline LL lgcd(LL a, LL b) { return b == 0 ? a : lgcd(b, a%b); } 25 inline LL llcm(LL a, LL b) { return a / lgcd(a, b)*b; } //a*b = gcd*lcm 26 inline int Max(int a, int b) { return a>b ? a : b; } 27 inline int Min(int a, int b) { return a>b ? b : a; } 28 inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } 29 inline int lcm(int a, int b) { return a / gcd(a, b)*b; } //a*b = gcd*lcm 30 const LL INF = 0x3f3f3f3f3f3f3f3f; 31 const LL mod = 1e9+7; 32 const double eps = 1e-8; 33 const int inf = 0x3f3f3f3f; 34 const int maxm = 10100; 35 const int maxn = 1050; 36 37 struct edge{ 38 int to, next, val, c; 39 }e[maxm<<2]; 40 41 int n, m; 42 int cnt, st, ed; 43 int linjie[maxn], pre[maxn], dis[maxn], path[maxn]; 44 45 void addedge(int u, int v, int w, int f) 46 { e[cnt].to=v; e[cnt].next=linjie[u]; e[cnt].c=f; e[cnt].val=w; linjie[u]=cnt++; } 47 48 void init() 49 { 50 cnt = 0; memset(linjie, -1, sizeof(linjie)); 51 st = 0; ed = 1010; 52 53 cin >> n >> m; 54 55 int x, y, f; 56 foe(i, 1, m) 57 { 58 scanf("%d %d %d", &x, &y, &f); 59 addedge(x, y, 1, f), addedge(y, x, 0, -f); 60 addedge(y, x, 1, f), addedge(x, y, 0, -f); 61 } 62 addedge(st, 1, 2, 0), addedge(1, st, 0, 0); 63 addedge(n, ed, 2, 0), addedge(ed, n, 0, 0); 64 } 65 66 bool spfa(int s, int t) 67 { 68 memset(pre, -1, sizeof(pre)); 69 memset(dis, 0x3f, sizeof(dis)); 70 dis[s] = 0; 71 72 queue<int> q; q.push(s); 73 while(!q.empty()) 74 { 75 int u = q.front(); q.pop(); 76 77 for ( int i = linjie[u]; ~i; i=e[i].next ) 78 { 79 int v = e[i].to, w = e[i].val, c = e[i].c; 80 if ( w>0 && dis[u]+c<dis[v] ) 81 { 82 dis[v] = dis[u]+c; 83 pre[v] = u; path[v] = i; 84 q.push(v); 85 } 86 } 87 } 88 89 if (pre[t] > 0) return true; 90 else return false; 91 } 92 93 int mif(int s, int t) 94 { 95 int cost = 0; 96 while(spfa(s, t)) 97 { 98 int ff = inf; 99 for ( int u = t; u != s; u = pre[u] ) 100 ff = Min(ff, e[path[u]].val); 101 102 cost += dis[t] * ff; 103 for ( int u = t; u != s; u = pre[u] ) 104 { 105 e[path[u]].val -= ff; 106 e[path[u]^1].val += ff; 107 } 108 } 109 110 return cost; 111 } 112 113 int main() 114 { 115 116 #ifndef ONLINE_JUDGE 117 freopen("input.txt", "r", stdin); 118 #endif 119 120 init(); 121 cout << mif(st, ed) << endl; 122 }