网络流
网络流
最新版模板:
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 #include <cstring> 5 6 const int N = 10010, M = 1000010, INF = 0x3f3f3f3f; 7 8 struct Edge { 9 int nex, v, c; 10 }edge[M << 1]; int top = 1; 11 12 int e[N], d[N]; 13 std::queue<int> Q; 14 15 inline void add(int x, int y, int z) { 16 top++; 17 edge[top].v = y; 18 edge[top].c = z; 19 edge[top].nex = e[x]; 20 e[x] = top; 21 22 top++; 23 edge[top].v = x; 24 edge[top].c = 0; 25 edge[top].nex = e[y]; 26 e[y] = top; 27 return; 28 } 29 30 inline bool BFS(int s, int t) { 31 memset(d, 0, sizeof(d)); 32 d[s] = 1; 33 Q.push(s); 34 while(!Q.empty()) { 35 int x = Q.front(); 36 Q.pop(); 37 for(int i = e[x]; i; i = edge[i].nex) { 38 int y = edge[i].v; 39 if(!edge[i].c || d[y]) { 40 continue; 41 } 42 d[y] = d[x] + 1; 43 Q.push(y); 44 } 45 } 46 return d[t]; 47 } 48 49 int DFS(int x, int t, int maxF) { 50 if(x == t) { 51 return maxF; 52 } 53 int ans = 0; 54 for(int i = e[x]; i; i = edge[i].nex) { 55 int y = edge[i].v; 56 if(!edge[i].c || d[x] + 1 != d[y]) { 57 continue; 58 } 59 int temp = DFS(y, t, std::min(edge[i].c, maxF - ans)); 60 if(!temp) { 61 d[y] = INF; 62 } 63 ans += temp; 64 edge[i].c -= temp; 65 edge[i ^ 1].c += temp; 66 if(ans == maxF) { 67 break; 68 } 69 } 70 return ans; 71 } 72 73 inline int solve(int s, int t) { 74 int ans = 0; 75 while(BFS(s, t)) { 76 ans += DFS(s, t, INF); 77 } 78 return ans; 79 } 80 81 int main() { 82 83 int n, m, s, t; 84 scanf("%d%d%d%d", &n, &m, &s, &t); 85 for(int i = 1, x, y, z; i <= m; i++) { 86 scanf("%d%d%d", &x, &y, &z); 87 add(x, y, z); 88 } 89 printf("%d", solve(s, t)); 90 return 0; 91 }
1 #include <cstdio> 2 #include <algorithm> 3 #include <queue> 4 #include <cstring> 5 6 const int N = 5010, M = 1000010, INF = 0x3f3f3f3f; 7 8 struct Edge { 9 int nex, v, c, len; 10 }edge[M << 1]; int top = 1; 11 12 int e[N], d[N], vis[N], pre[N], flow[N]; 13 std::queue<int> Q; 14 15 inline void add(int x, int y, int z, int w) { 16 top++; 17 edge[top].v = y; 18 edge[top].c = z; 19 edge[top].len = w; 20 edge[top].nex = e[x]; 21 e[x] = top; 22 23 top++; 24 edge[top].v = x; 25 edge[top].c = 0; 26 edge[top].len = -w; 27 edge[top].nex = e[y]; 28 e[y] = top; 29 return; 30 } 31 32 inline bool SPFA(int s, int t) { 33 memset(d, 0x3f, sizeof(d)); 34 d[s] = 0; 35 flow[s] = INF; 36 vis[s] = 1; 37 Q.push(s); 38 while(!Q.empty()) { 39 int x = Q.front(); 40 Q.pop(); 41 vis[x] = 0; 42 for(int i = e[x]; i; i = edge[i].nex) { 43 int y = edge[i].v; 44 if(edge[i].c && d[y] > d[x] + edge[i].len) { 45 d[y] = d[x] + edge[i].len; 46 pre[y] = i; 47 flow[y] = std::min(flow[x], edge[i].c); 48 if(!vis[y]) { 49 vis[y] = 1; 50 Q.push(y); 51 } 52 } 53 } 54 } 55 return d[t] < INF; 56 } 57 58 inline void update(int s, int t) { 59 int temp = flow[t]; 60 while(t != s) { 61 int i = pre[t]; 62 edge[i].c -= temp; 63 edge[i ^ 1].c += temp; 64 t = edge[i ^ 1].v; 65 } 66 return; 67 } 68 69 inline int solve(int s, int t, int &cost) { 70 int ans = 0; 71 cost = 0; 72 while(SPFA(s, t)) { 73 ans += flow[t]; 74 cost += flow[t] * d[t]; 75 update(s, t); 76 } 77 return ans; 78 } 79 80 int main() { 81 82 int n, m, s, t; 83 scanf("%d%d%d%d", &n, &m, &s, &t); 84 for(int i = 1, x, y, z, w; i <= m; i++) { 85 scanf("%d%d%d%d", &x, &y, &z, &w); 86 add(x, y, z, w); 87 } 88 int cost; 89 int ans = solve(s, t, cost); 90 printf("%d %d", ans, cost); 91 return 0; 92 }
如果只要求最小费用,不要求最大流的话,在while(SPFA)中d[t]>0时break即可。例:九月的咖啡店
下面我们来看一道模板题:洛谷P3376
1 #include <cstdio> 2 #include <queue> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const int N = 10010,M = 100010; 7 struct Dinic 8 { 9 struct Edge 10 { 11 int u,v,c,next; 12 }edge[M<<1];int top=1; 13 int e[N],deep[N],cur[N]; 14 void add(int x,int y,int z) 15 { 16 top++; 17 edge[top].u=x; 18 edge[top].v=y; 19 edge[top].c=z; 20 edge[top].next=e[x]; 21 e[x]=top; 22 top++; 23 edge[top].u=y; 24 edge[top].v=x; 25 edge[top].c=0; 26 edge[top].next=e[y]; 27 e[y]=top; 28 return; 29 } 30 queue<int>Q; 31 bool BFS(int s,int t) 32 { 33 memset(deep,0,sizeof(deep)); 34 while(!Q.empty()) Q.pop(); 35 deep[s]=1; 36 Q.push(s); 37 while(!Q.empty()) 38 { 39 int op=Q.front(); 40 Q.pop(); 41 for(int i=e[op];i;i=edge[i].next) if(edge[i].c) 42 { 43 int ed=edge[i].v; 44 if(deep[ed]) continue; 45 deep[ed]=deep[op]+1; 46 if(ed==t) return true; 47 Q.push(ed); 48 } 49 } 50 return false; 51 } 52 int DFS(int op,int t,int maxF) 53 { 54 if(op==t) return maxF; 55 int ans=0,i=(cur[op]?cur[op]:e[op]);///当前弧优化 56 for(;i;i=edge[i].next) if(edge[i].c) 57 { 58 int ed=edge[i].v; 59 if(deep[ed]!=deep[op]+1) continue;///注意这里,不加爆0 60 int temp=DFS(ed,t,min(maxF-ans,edge[i].c)); 61 if(!temp) {deep[ed]=0;continue;} ///注意这里,优化1/3速度 62 ans+=temp; 63 edge[i].c-=temp; 64 edge[i^1].c+=temp; 65 cur[op]=i; 66 if(ans==maxF) return ans; 67 } 68 cur[op]=0; 69 return ans; 70 } 71 int solve(int s,int t) 72 { 73 int ans=0; 74 while(BFS(s,t)) 75 { 76 memset(cur,0,sizeof(cur)); 77 ans+=DFS(s,t,0x7f7f7f7f); 78 } 79 return ans; 80 } 81 }dinic; 82 83 int main() 84 { 85 int n,m,s,t; 86 scanf("%d%d%d%d",&n,&m,&s,&t); 87 int x,y,z; 88 while(m--) 89 { 90 scanf("%d%d%d",&x,&y,&z); 91 dinic.add(x,y,z); 92 } 93 printf("%d",dinic.solve(s,t)); 94 return 0; 95 }
发现费用流的模板没放...赶紧放一个先。
Dinic怎么搞都会挂的奇惨无比,还是用EK比较稳。
这个模板是EK的。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <algorithm> 5 typedef long long LL; 6 const int N = 5010, M = 50010; 7 const LL INF = 1ll << 62; 8 9 int n; 10 11 struct Dinic { 12 struct Edge { 13 int v, nex; 14 LL c, len; 15 Edge(int v = 0, int nex = 0, LL c = 0, LL len = 0) { 16 this->v = v; 17 this->nex = nex; 18 this->c = c; 19 this->len = len; 20 } 21 }edge[M << 1]; int top; 22 int e[N], vis[N], pre[N]; 23 LL dis[N], flow[N]; 24 Dinic() { 25 top = 1; 26 memset(e, 0, sizeof(e)); 27 } 28 inline void add(int x, int y, LL z, LL len) { 29 ++top; 30 edge[top].v = y; 31 edge[top].c = z; 32 edge[top].len = len; 33 edge[top].nex = e[x]; 34 e[x] = top++; 35 edge[top].v = x; 36 edge[top].c = 0; 37 edge[top].nex = e[y]; 38 edge[top].len = -len; /// len 39 e[y] = top; 40 return; 41 } 42 inline bool SPFA(int s, int t) { 43 for(int i = 1; i <= n; i++) { 44 dis[i] = INF; 45 } 46 memset(vis, 0, sizeof(vis)); 47 memset(pre, 0, sizeof(pre)); 48 memset(flow, 0, sizeof(flow)); 49 dis[s] = 0; 50 vis[s] = 1; 51 flow[s] = INF; 52 std::queue<int> Q; 53 Q.push(s); 54 while(!Q.empty()) { 55 int x = Q.front(); 56 //printf("SPFA: %d\n", x); 57 Q.pop(); 58 vis[x] = 0; 59 for(int i = e[x]; i; i = edge[i].nex) { 60 int y = edge[i].v; 61 if(dis[y] > dis[x] + edge[i].len && edge[i].c) { 62 dis[y] = dis[x] + edge[i].len; 63 pre[y] = i; 64 flow[y] = std::min(flow[x], edge[i].c); 65 if(vis[y]) { 66 continue; 67 } 68 vis[y] = 1; 69 Q.push(y); 70 } 71 } 72 } 73 return dis[t] < INF; 74 } 75 inline void update(int s, int t) { 76 int i, temp = flow[t]; 77 while(t != s) { 78 //printf("update: %d\n", t); 79 i = pre[t]; 80 edge[i].c -= temp; 81 edge[i ^ 1].c += temp; 82 t = edge[i ^ 1].v; 83 } 84 return; 85 } 86 void solve(int s, int t, LL &maxF, LL &cost) { 87 maxF = cost = 0; 88 while(SPFA(s, t)){ 89 update(s, t); 90 maxF += flow[t]; 91 cost += flow[t] * dis[t]; 92 } 93 return; 94 } 95 }D; 96 97 int main() { 98 int m, s, t; 99 scanf("%d%d%d%d", &n, &m, &s, &t); 100 int x, y; 101 LL z, len; 102 for(int i = 1; i <= m; i++) { 103 scanf("%d%d%lld%lld", &x, &y, &z, &len); 104 D.add(x, y, z, len); 105 } 106 LL a, b; 107 D.solve(s, t, a, b); 108 printf("%lld %lld", a, b); 109 return 0; 110 }
下面我们来看一道裸题:洛谷P4001 bzoj1001 狼抓兔子
得出结论:无向图只需把反向边的流量也建为z即可 。
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const int N = 1000010,M = 3000010; 7 int read() 8 { 9 int ans=0; 10 char ch=getchar(); 11 while(ch<'0'||ch>'9') ch=getchar(); 12 while(ch>='0'&&ch<='9') ans=ans*10,ans+=ch-'0',ch=getchar(); 13 return ans; 14 } 15 struct Dinic 16 { 17 struct Edge 18 { 19 int u,v,c,next; 20 }edge[M<<1];int top=1; 21 int e[N],deep[N],cur[N]; 22 void add(int x,int y,int z) 23 { 24 top++; 25 edge[top].u=x; 26 edge[top].v=y; 27 edge[top].c=z; 28 edge[top].next=e[x]; 29 e[x]=top; 30 top++; 31 edge[top].u=y; 32 edge[top].v=x; 33 edge[top].c=z; 34 edge[top].next=e[y]; 35 e[y]=top; 36 return; 37 } 38 queue<int>Q; 39 bool BFS(int s,int t) 40 { 41 memset(deep,0,sizeof(deep)); 42 while(!Q.empty()) Q.pop(); 43 deep[s]=1; 44 Q.push(s); 45 while(!Q.empty()) 46 { 47 int op=Q.front(); 48 Q.pop(); 49 for(int i=e[op];i;i=edge[i].next) if(edge[i].c) 50 { 51 int ed=edge[i].v; 52 if(deep[ed]) continue; 53 deep[ed]=deep[op]+1; 54 if(ed==t) return true; 55 Q.push(ed); 56 } 57 } 58 return false; 59 } 60 int DFS(int op,int t,int maxF) 61 { 62 if(op==t) return maxF; 63 int ans=0,i=(cur[op]?cur[op]:e[op]); 64 for(;i;i=edge[i].next) if(edge[i].c) 65 { 66 int ed=edge[i].v; 67 if(deep[ed]!=deep[op]+1) continue; 68 int temp=DFS(ed,t,min(maxF-ans,edge[i].c)); 69 if(!temp) {deep[ed]=-1;continue;} 70 ans+=temp; 71 edge[i].c-=temp; 72 edge[i^1].c+=temp; 73 cur[op]=i; 74 if(ans==maxF) return ans; 75 } 76 cur[op]=0; 77 return ans; 78 } 79 int solve(int s,int t) 80 { 81 int ans=0; 82 while(BFS(s,t)) 83 { 84 memset(cur,0,sizeof(cur)); 85 ans+=DFS(s,t,0x7f7f7f7f); 86 } 87 return ans; 88 } 89 }dinic; 90 int main() 91 { 92 int n=read();int m=read(); 93 int x; 94 for(int i=1;i<=n;i++) 95 { 96 for(int j=1;j<m;j++) 97 { 98 x=read(); 99 dinic.add((i-1)*m+j,(i-1)*m+j+1,x); 100 //dinic.add((i-1)*m+j+1,(i-1)*m+j,x); 101 } 102 } 103 for(int i=1;i<n;i++) 104 { 105 for(int j=1;j<=m;j++) 106 { 107 x=read(); 108 dinic.add((i-1)*m+j,i*m+j,x); 109 //dinic.add(i*m+j,(i-1)*m+j,x); 110 } 111 } 112 for(int i=1;i<n;i++) 113 { 114 for(int j=1;j<m;j++) 115 { 116 x=read(); 117 dinic.add((i-1)*m+j,i*m+j+1,x); 118 //dinic.add(i*m+j+1,(i-1)*m+j,x); 119 } 120 } 121 printf("%d",dinic.solve(1,m*n)); 122 return 0; 123 }
就是跑的有点慢......可以建对偶图转最短路,不过我显然没有那个兴致。
下面我们来看一道入门题:poj3498 March of the Penguins
题意:
做法:因为n很小所以可以支持我们枚举终点T。
我们尝试用一流量表示一只penguin的去向。
把每个冰块拆点来限制mi
从S往入点连流量ni的边。
入点向出点连流量mi的边。
距离小于d的连流量INF的边。
然后查看从S到T的入点是否满流即可。
注意:
每次枚举终点时把流量改回来。
空间开足。
点从0开始编号。
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 const int N = 2100, M = 100010, INF = 0x3f3f3f3f; 5 6 struct Edge { 7 int v, nex, f; 8 }edge[M << 1]; int top = 1; 9 10 int e[N], d[N], a[N], b[N]; 11 double xx[N], yy[N]; 12 13 inline void add(int x, int y, int z) { 14 edge[++top].v = y; 15 edge[top].f = z; 16 edge[top].nex = e[x]; 17 e[x] = top++; 18 edge[top].v = x; 19 edge[top].f = 0; 20 edge[top].nex = e[y]; 21 e[y] = top; 22 return; 23 } 24 25 inline bool BFS(int s, int t) { 26 memset(d, 0, sizeof(b)); 27 std::queue<int> Q; 28 d[s] = 1; 29 Q.push(s); 30 while(!Q.empty()) { 31 int x = Q.front(); 32 Q.pop(); 33 for(int i = e[x]; i; i = edge[i].nex) { 34 int y = edge[i].v; 35 if(!edge[i].f || d[y]) { 36 continue; 37 } 38 d[y] = d[x] + 1; 39 Q.push(y); 40 } 41 } 42 return d[t]; 43 } 44 45 inline int DFS(int x, int t, int maxF) { 46 if(x == t) { 47 return maxF; 48 } 49 int ans = 0; 50 for(int i = e[x]; i; i = edge[i].nex) { 51 int y = edge[i].v; 52 if(d[x] + 1 != d[y] || !edge[i].f) { 53 continue; 54 } 55 int temp = DFS(y, t, std::min(edge[i].f, maxF - ans)); 56 if(!temp) { 57 d[y] = 0; 58 } 59 ans += temp; 60 edge[i].f -= temp; 61 edge[i ^ 1].f += temp; 62 if(ans == maxF) { 63 return ans; 64 } 65 } 66 return ans; 67 } 68 69 inline int dinic(int s, int t) { 70 int ans = 0; 71 while(BFS(s, t)) { 72 ans += DFS(s, t, 0x7f7f7f7f); 73 } 74 return ans; 75 } 76 77 inline double dis2(int i, int j) { 78 return (xx[i] - xx[j]) * (xx[i] - xx[j]) + (yy[i] - yy[j]) * (yy[i] - yy[j]); 79 } 80 81 inline void clear() { 82 memset(e, 0, sizeof(e)); 83 top = 1; 84 return; 85 } 86 87 inline void solve() { 88 int n, tot = 0; 89 double D; 90 scanf("%d%lf", &n, &D); 91 int S = n << 1 | 1; 92 for(int i = 1; i <= n; i++) { 93 scanf("%lf%lf%d%d", &xx[i], &yy[i], &a[i], &b[i]); 94 tot += a[i]; 95 } 96 bool f = 0; 97 clear(); 98 for(int i = 1; i <= n; i++) { 99 add(S, i, a[i]); 100 add(i, i + n, b[i]); 101 for(int j = 1; j < i; j++) { 102 if(dis2(i, j) <= D * D) { 103 add(i + n, j, INF); 104 add(j + n, i, INF); 105 } 106 } 107 } 108 for(int i = 1; i <= n; i++) { 109 for(int j = 2; j <= top; j += 2) { 110 edge[j ^ 1].f = 0; 111 int x = edge[j ^ 1].v; 112 int y = edge[j].v; 113 if(x == S) { 114 edge[j].f = a[y]; 115 } 116 else if(x + n == y) { 117 edge[j].f = b[x]; 118 } 119 else { 120 edge[j].f = INF; 121 } 122 } 123 if(dinic(S, i) == tot) { 124 f = 1; 125 printf("%d ", i - 1); 126 } 127 } 128 if(!f) { 129 printf("-1"); 130 } 131 puts(""); 132 return; 133 } 134 135 int main() { 136 int T; 137 scanf("%d", &T); 138 for(int i = 1; i <= T; i++) { 139 solve(); 140 } 141 return 0; 142 }
poj1149 PIGS
题意:
我们尝试用一流量代表一头猪的去向。
由于整个过程可以由顾客来划分成N个阶段,所以考虑分层图。
购买的那几个猪圈之间建立一个中间点,作为中转站,向T连流量为顾客购买力的边。
S向第一层的点连流量为初始猪数的边。
各点在各层之间纵向连边,流量INF。有中转点就在中转点那里过一下,流量还是INF。
然后输出最大流即可。
优化建图:我们发现有很多层之间的INF链,每条链都缩为一条边。
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 const int N = 100100, M = 1000010, INF = 0x3f3f3f3f; 5 6 struct Edge { 7 int v, nex, f; 8 }edge[M << 1]; int top = 1; 9 10 int e[N], d[N], nt, last[N]; 11 12 inline void add(int x, int y, int z) { 13 edge[++top].v = y; 14 edge[top].f = z; 15 edge[top].nex = e[x]; 16 e[x] = top++; 17 edge[top].v = x; 18 edge[top].f = 0; 19 edge[top].nex = e[y]; 20 e[y] = top; 21 return; 22 } 23 24 inline bool BFS(int s, int t) { 25 memset(d, 0, sizeof(d)); 26 std::queue<int> Q; 27 d[s] = 1; 28 Q.push(s); 29 while(!Q.empty()) { 30 int x = Q.front(); 31 Q.pop(); 32 for(int i = e[x]; i; i = edge[i].nex) { 33 int y = edge[i].v; 34 if(!edge[i].f || d[y]) { 35 continue; 36 } 37 d[y] = d[x] + 1; 38 Q.push(y); 39 } 40 } 41 return d[t]; 42 } 43 44 inline int DFS(int x, int t, int maxF) { 45 if(x == t) { 46 return maxF; 47 } 48 int ans = 0; 49 for(int i = e[x]; i; i = edge[i].nex) { 50 int y = edge[i].v; 51 if(d[x] + 1 != d[y] || !edge[i].f) { 52 continue; 53 } 54 int temp = DFS(y, t, std::min(edge[i].f, maxF - ans)); 55 if(!temp) { 56 d[y] = 0; 57 } 58 ans += temp; 59 edge[i].f -= temp; 60 edge[i ^ 1].f += temp; 61 if(ans == maxF) { 62 return ans; 63 } 64 } 65 return ans; 66 } 67 68 inline int dinic(int s, int t) { 69 int ans = 0; 70 while(BFS(s, t)) { 71 ans += DFS(s, t, 0x7f7f7f7f); 72 } 73 return ans; 74 } 75 76 int main() { 77 int m, n, S = N - 1, T = N - 2; 78 scanf("%d%d", &n, &m); 79 for(int i = 1, x; i <= n; i++) { 80 scanf("%d", &x); 81 add(S, i, x); 82 last[i] = i; 83 } 84 nt = n; 85 for(int i = 1, a, b, t, x; i <= m; i++) { 86 scanf("%d", &a); 87 t = ++nt; 88 for(int j = 1; j <= a; j++) { 89 scanf("%d", &x); 90 add(last[x], t, INF); 91 last[x] = t; 92 } 93 scanf("%d", &b); 94 add(t, T, b); 95 } 96 printf("%d", dinic(S, T)); 97 return 0; 98 }
SGU 326 Perspective
这个OJ就是毒瘤...
题意:
组外的随便处理一下,跟1有关的也处理了。
现在只需要解决剩下的组内比赛了。
我们尝试用一流量代表胜一场。
那么对于i与j之间的比赛,建立节点t。
S向t连流量为a[i][j]的边,t向i,j连流量为a[i][j]的边。
每个队代表的点向T连最大剩余胜场的边。
若 最大流 == 比赛场数 则输出YES否则NO
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 const int N = 1000, M = 100010, INF = 0x3f3f3f3f; 5 6 struct Edge { 7 int v, nex, f; 8 }edge[M << 1]; int top = 1; 9 10 int e[N], d[N], win[N], rest[N]; 11 int a[30][30]; 12 13 inline void add(int x, int y, int z) { 14 edge[++top].v = y; 15 edge[top].f = z; 16 edge[top].nex = e[x]; 17 e[x] = top++; 18 edge[top].v = x; 19 edge[top].f = 0; 20 edge[top].nex = e[y]; 21 e[y] = top; 22 return; 23 } 24 25 inline bool BFS(int s, int t) { 26 memset(d, 0, sizeof(d)); 27 std::queue<int> Q; 28 d[s] = 1; 29 Q.push(s); 30 while(!Q.empty()) { 31 int x = Q.front(); 32 Q.pop(); 33 for(int i = e[x]; i; i = edge[i].nex) { 34 int y = edge[i].v; 35 if(!edge[i].f || d[y]) { 36 continue; 37 } 38 d[y] = d[x] + 1; 39 Q.push(y); 40 } 41 } 42 return d[t]; 43 } 44 45 inline int DFS(int x, int t, int maxF) { 46 if(x == t) { 47 return maxF; 48 } 49 int ans = 0; 50 for(int i = e[x]; i; i = edge[i].nex) { 51 int y = edge[i].v; 52 if(d[x] + 1 != d[y] || !edge[i].f) { 53 continue; 54 } 55 int temp = DFS(y, t, std::min(edge[i].f, maxF - ans)); 56 if(!temp) { 57 d[y] = 0; 58 } 59 ans += temp; 60 edge[i].f -= temp; 61 edge[i ^ 1].f += temp; 62 if(ans == maxF) { 63 return ans; 64 } 65 } 66 return ans; 67 } 68 69 inline int dinic(int s, int t) { 70 int ans = 0; 71 while(BFS(s, t)) { 72 ans += DFS(s, t, 0x7f7f7f7f); 73 } 74 return ans; 75 } 76 77 int main() { 78 int n; 79 scanf("%d", &n); 80 for(int i = 1; i <= n; i++) { 81 scanf("%d", &win[i]); 82 } 83 for(int i = 1; i <= n; i++) { 84 scanf("%d", &rest[i]); 85 } 86 for(int i = 1, t; i <= n; i++) { 87 t = 0; 88 for(int j = 1; j <= n; j++) { 89 scanf("%d", &a[i][j]); 90 t += a[i][j]; 91 } 92 if(t < rest[i]) { 93 if(i == 1) { 94 win[i] += rest[i] - t; 95 } 96 rest[i] = t; 97 } 98 if(i > 1 && a[i][1]) { 99 win[1] += a[i][1]; 100 rest[i] -= a[i][1]; 101 rest[1] -= a[i][1]; 102 a[i][1] = a[1][i] = 0; 103 } 104 } 105 106 int S = n * n + 10 * n + 1; 107 int T = S + 1, tot = 0, t = 0; 108 int B = n * n + 3 * n; 109 for(int i = 2; i <= n; i++) { 110 for(int j = 2; j < i; j++) { 111 add(S, ++t, a[i][j]); 112 add(t, i + B, a[i][j]); 113 add(t, j + B, a[i][j]); 114 } 115 add(i + B, T, win[1] - win[i]); 116 tot += rest[i]; 117 } 118 119 tot = tot >> 1; 120 int ans = dinic(S, T); 121 122 if(ans == tot) { 123 printf("YES"); 124 } 125 else { 126 printf("NO"); 127 } 128 return 0; 129 }
poj3281 Dining
题意:
这个题有两个匹配条件,怎么办?
奶牛放中间即可。两边是食物饮料。
然后奶牛还要拆点来限流为1。
1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 const int N = 1000, M = 100010, INF = 0x3f3f3f3f; 5 6 struct Edge { 7 int v, nex, f; 8 }edge[M << 1]; int top = 1; 9 10 int e[N], d[N], win[N], rest[N]; 11 int a[30][30]; 12 13 inline void add(int x, int y, int z) { 14 edge[++top].v = y; 15 edge[top].f = z; 16 edge[top].nex = e[x]; 17 e[x] = top++; 18 edge[top].v = x; 19 edge[top].f = 0; 20 edge[top].nex = e[y]; 21 e[y] = top; 22 return; 23 } 24 25 inline bool BFS(int s, int t) { 26 memset(d, 0, sizeof(d)); 27 std::queue<int> Q; 28 d[s] = 1; 29 Q.push(s); 30 while(!Q.empty()) { 31 int x = Q.front(); 32 Q.pop(); 33 for(int i = e[x]; i; i = edge[i].nex) { 34 int y = edge[i].v; 35 if(!edge[i].f || d[y]) { 36 continue; 37 } 38 d[y] = d[x] + 1; 39 Q.push(y); 40 } 41 } 42 return d[t]; 43 } 44 45 inline int DFS(int x, int t, int maxF) { 46 if(x == t) { 47 return maxF; 48 } 49 int ans = 0; 50 for(int i = e[x]; i; i = edge[i].nex) { 51 int y = edge[i].v; 52 if(d[x] + 1 != d[y] || !edge[i].f) { 53 continue; 54 } 55 int temp = DFS(y, t, std::min(edge[i].f, maxF - ans)); 56 if(!temp) { 57 d[y] = 0; 58 } 59 ans += temp; 60 edge[i].f -= temp; 61 edge[i ^ 1].f += temp; 62 if(ans == maxF) { 63 return ans; 64 } 65 } 66 return ans; 67 } 68 69 inline int dinic(int s, int t) { 70 int ans = 0; 71 while(BFS(s, t)) { 72 ans += DFS(s, t, 0x7f7f7f7f); 73 } 74 return ans; 75 } 76 77 int main() { 78 int n, A, B; 79 scanf("%d%d%d", &n, &A, &B); 80 for(int i = 1, x, a, b; i <= n; i++) { 81 scanf("%d%d", &a, &b); 82 for(int j = 1; j <= a; j++) { 83 scanf("%d", &x); 84 add(x, i + A, 1); 85 } 86 for(int j = 1; j <= b; j++) { 87 scanf("%d", &x); 88 add(i + A + n, x + A + n + n, 1); 89 } 90 add(i + A, i + A + n, 1); 91 } 92 int S = A + B + n + n + 1; 93 int T = S + 1; 94 for(int i = 1; i <= A; i++) { 95 add(S, i, 1); 96 } 97 for(int i = 1; i <= B; i++) { 98 add(i + A + n + n, T, 1); 99 } 100 printf("%d", dinic(S, T)); 101 return 0; 102 }
现在我们来看一个经典模型:棋盘车放置。
车可以攻击一行一列,问m * n的棋盘上最多能放置几个车?
min(m,n)啊,这不是废话......
那么加上一些障碍物,再加上一些地方不能放呢?
我们尝试用一流量来代表一个车的放置。
然后把所有行和列抽象成点,每个能放车的位置就在其所属的行列之间连流量为1的边。
行列和ST之间连流量为1的边。
如果有车的攻击无法越过的障碍物,相当于把一行分成了两行。列同理。
例题:洛谷P1263 宫廷守卫
1 #include <cstdio> 2 #include <queue> 3 #include <algorithm> 4 #include <cstring> 5 6 const int N = 210, INF = 0x7f7f7f7f; 7 8 struct Edge { 9 int nex, v, c; 10 }edge[N * N * 2]; int top = 1; 11 12 int e[N * N], d[N * N], eg[N][N], G[N][N]; 13 std::pair<int, int> pr[N][N]; 14 15 inline void add(int x, int y, int z) { 16 top++; 17 edge[top].v = y; 18 edge[top].c = z; 19 edge[top].nex = e[x]; 20 e[x] = top; 21 top++; 22 edge[top].v = x; 23 edge[top].c = 0; 24 edge[top].nex = e[y]; 25 e[y] = top; 26 return; 27 } 28 29 inline bool BFS(int s, int t) { 30 std::queue<int> Q; 31 Q.push(s); 32 memset(d, 0, sizeof(d)); 33 d[s] = 1; 34 while(!Q.empty()) { 35 int x = Q.front(); 36 Q.pop(); 37 for(int i = e[x]; i; i = edge[i].nex) { 38 int y = edge[i].v; 39 if(!edge[i].c || d[y]) { 40 continue; 41 } 42 d[y] = d[x] + 1; 43 Q.push(y); 44 } 45 } 46 return d[t]; 47 } 48 49 int DFS(int x, int t, int maxF) { 50 if(x == t) { 51 return maxF; 52 } 53 int ans = 0; 54 for(int i = e[x]; i; i = edge[i].nex) { 55 int y = edge[i].v; 56 if(!edge[i].c || d[x] + 1 != d[y]) { 57 continue; 58 } 59 int temp = DFS(y, t, std::min(edge[i].c, maxF - ans)); 60 if(!temp) { 61 d[y] = 0; 62 continue; 63 } 64 ans += temp; 65 edge[i].c -= temp; 66 edge[i ^ 1].c += temp; 67 if(ans == maxF) { 68 break; 69 } 70 } 71 return ans; 72 } 73 74 inline int solve(int s, int t) { 75 int ans = 0; 76 while(BFS(s, t)) { 77 ans += DFS(s, t, INF); 78 } 79 return ans; 80 } 81 82 int main() { 83 int n, m; 84 scanf("%d%d", &n, &m); 85 for(int i = 1; i <= n; i++) { 86 for(int j = 1; j <= m; j++) { 87 scanf("%d", &G[i][j]); 88 } 89 } 90 91 // prework 92 int lm = 1; 93 bool f = 0; 94 for(int i = 1; i <= n; i++) { /// - 95 if(f) { 96 f = 0; 97 lm++; 98 } 99 for(int j = 1; j <= m; j++) { 100 if(G[i][j] == 2 && f) { 101 lm++; 102 f = 0; 103 } 104 else { 105 pr[i][j].first = lm; 106 f = 1; 107 } 108 } 109 } 110 int t = 1; 111 f = 0; 112 for(int j = 1; j <= m; j++) { 113 if(f) { 114 f = 0; 115 t++; 116 } 117 for(int i = 1; i <= n; i++) { 118 if(G[i][j] == 2 && f) { 119 t++; 120 f = 0; 121 } 122 else { 123 pr[i][j].second = t; 124 f = 1; 125 } 126 } 127 } 128 // - : 1 ... lm | : lm + 1 ... t 129 for(int i = 1; i <= n; i++) { 130 for(int j = 1; j <= m; j++) { 131 if(!G[i][j]) { 132 eg[i][j] = top + 1; 133 add(pr[i][j].first, pr[i][j].second + lm, 1); 134 } 135 } 136 } 137 int S = lm + t + 1; 138 int T = S + 1; 139 for(int i = 1; i <= lm; i++) { 140 add(S, i, 1); 141 } 142 for(int i = 1; i <= t; i++) { 143 add(lm + i, T, 1); 144 } 145 146 int ans = solve(S, T); 147 printf("%d \n", ans); 148 149 for(int i = 1; i <= n; i++) { 150 for(int j = 1; j <= m; j++) { 151 if(!G[i][j] && !edge[eg[i][j]].c) { 152 printf("%d %d \n", i, j); 153 } 154 } 155 } 156 157 return 0; 158 }
最后来一个总结:
网络流主要就是各种建图,基本靠背,见多识广。
常见的有:直接把两种事物分开连边,每天/每个位置建点,横着流竖着流,比赛向双方连边流量表示胜场,行向列连边,点向行列连边,最小天数时每天加点加边,拆点限流,一流量代表一人/一物,各种拆点,费用流解线性规划,费用与流量的平方有关拆成1357,先构造一组解然后网络流(上下界)调整,混合图欧拉回路(随意定向)。
常见模型有:最大流,最小割,费用流,最大权闭合子图(正负向源汇连边求最小割),二分图最大匹配(最大流),二分图最小点覆盖(最小割),二分图最大独立集(减去点覆盖),DAG最少链覆盖(n-转二分图最大匹配(边数)),DAG最少可重链覆盖(floyd传递连通性),DAG最长反链(DAG最大独立集)(等于最少链覆盖),上下界。