网络流

网络流

网络流24题  上下界网络流 最大权闭合子图 

最新版模板:

 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 }
Dinic最大流
 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 }
EK最小费用最大流

如果只要求最小费用,不要求最大流的话,在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

发现费用流的模板没放...赶紧放一个先。

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 }
洛谷P3381 AC代码

下面我们来看一道裸题:洛谷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 }
AC代码

就是跑的有点慢......可以建对偶图转最短路,不过我显然没有那个兴致。 


下面我们来看一道入门题:poj3498 March of the Penguins

题意:

一堆企鹅要碰面,现在有N块冰块,第i个冰块坐标为(xi,yi),上面有企鹅ni个,能承受企鹅在上面起跳mi次,并且知道企鹅最大跳距离为d。你要帮助企鹅找到所有可能的聚集地点。
冰块 1 ≤ N ≤ 100
 0 ≤ ni ≤ 10, 1 ≤ mi ≤ 200

做法:因为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 }
AC代码

poj1149 PIGS

题意:

有 M 个猪圈,每个猪圈里初始时有若干头猪。开始所有猪圈都是关闭的。依次来了 N 个顾客,每个顾客分别会打开指定的几个猪圈,从中买若干头猪。每个顾客分别都有他能够买的数量的上限。每个顾客走后,他打开的那些猪圈中的猪,都可以被任意地调换到其它开着的猪圈里,然后所有猪圈重新关上。  问总共最多能卖出多少头猪。
1 <= N <= 100
1 <= M <= 1000

我们尝试用一流量代表一头猪的去向。

由于整个过程可以由顾客来划分成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 }
AC代码

 SGU 326 Perspective 

这个OJ就是毒瘤...

题意:

NBA 某小组内有 N 支球队,小组内以及小组间已经进行了若干场比赛。现在给出这 N 支球队目前胜利的场数、还剩多少场没有比(包括小组内和小组间)以及小组内任意两支球队之间还剩多少场没有比,存在maze[i][j]中, 问能否合理安排剩下的所有比赛,使得球队 1 最后胜利的场数至少大于等于小组内任何一支其他球队。
1 <= N <= 20

组外的随便处理一下,跟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

题意:

有F种食物和D种饮料,每种食物或饮料只能供一头牛享用,且每头牛只享用一种食物和一种饮料。现在有N头牛,每头牛都有自己喜欢的食物种类列表和饮料种类列表,问最多能使几头牛同时享用到自己喜欢的食物和饮料。
1 ≤ F ≤ 100
1 ≤ D ≤ 100
1 ≤ N ≤ 100 

这个题有两个匹配条件,怎么办?

奶牛放中间即可。两边是食物饮料。

然后奶牛还要拆点来限流为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 }
AC代码

现在我们来看一个经典模型:棋盘车放置。

车可以攻击一行一列,问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 }
AC代码

最后来一个总结:

网络流主要就是各种建图,基本靠背,见多识广。

常见的有:直接把两种事物分开连边,每天/每个位置建点,横着流竖着流,比赛向双方连边流量表示胜场,行向列连边,点向行列连边,最小天数时每天加点加边,拆点限流,一流量代表一人/一物,各种拆点,费用流解线性规划费用与流量的平方有关拆成1357,先构造一组解然后网络流(上下界)调整,混合图欧拉回路(随意定向)。

常见模型有:最大流,最小割,费用流,最大权闭合子图(正负向源汇连边求最小割),二分图最大匹配(最大流),二分图最小点覆盖(最小割),二分图最大独立集(减去点覆盖),DAG最少链覆盖(n-转二分图最大匹配(边数)),DAG最少可重链覆盖(floyd传递连通性),DAG最长反链(DAG最大独立集)(等于最少链覆盖),上下界。

 

posted @ 2018-04-13 13:05  huyufeifei  阅读(90)  评论(0编辑  收藏