(1)飞行员配对方案问题:二分图最大匹配。
思路:略。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #define MAXN 1010
4 int cx[MAXN], cy[MAXN];
5 int first[MAXN], next[MAXN], v[MAXN], e;
6 bool vis[MAXN];
7 inline void addEdge(int x, int y) {
8 v[e] = y;
9 next[e] = first[x];
10 first[x] = e++;
11 }
12 int path(int x) {
13 int i;
14 int y;
15 for (i = first[x]; i != -1; i = next[i]) {
16 y = v[i];
17 if (!vis[y]) {
18 vis[y] = true;
19 if (cy[y] == -1 || path(cy[y])) {
20 cx[x] = y;
21 cy[y] = x;
22 return 1;
23 }
24 }
25 }
26 return 0;
27 }
28 int main() {
29 int n, m;
30 int i;
31 int x, y;
32 int ans;
33 while (~scanf("%d%d", &m, &n)) {
34 e = 0;
35 memset(first, -1, sizeof(first));
36 while (scanf("%d%d", &x, &y), x != -1) {
37 addEdge(x, y);
38 }
39 ans = 0;
40 memset(cx, -1, sizeof(cx));
41 memset(cy, -1, sizeof(cy));
42 for (i = 1; i <= m; i++) {
43 memset(vis, false, sizeof(vis));
44 ans += path(i);
45 }
46 if (ans) {
47 printf("%d\n", ans);
48 for (i = 1; i <= m; i++) {
49 if (cx[i] != -1) {
50 printf("%d %d\n", i, cx[i]);
51 }
52 }
53 } else {
54 puts("No Solution!");
55 }
56 }
57 return 0;
58 }
(2)太空飞行计划问题:最大权闭合图。
思路:
1。源向实验连边,流量为收益。
2。仪器向汇连边,流量为消耗。
3。实验向仪器连边,流量为无穷。
4。所有实验的收益-最大流=最大收益。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #include<algorithm>
5 #include<iostream>
6 #define MAXL 1010
7 #define MAXN 100010
8 #define MAXM 1000010
9 #define oo 0x7FFFFFFF
10 using namespace std;
11 int first[MAXN], next[MAXM], v[MAXM], cost[MAXM], e;
12 int n;
13 int src, des;
14
15 bool flag;
16 bool vis[MAXL];
17 char str[MAXN];
18 vector<int> g[MAXL];
19 vector<int> test;
20 vector<int> app;
21 int a[MAXL];
22 inline void addEdge(int x, int y, int val) {
23 v[e] = y;
24 cost[e] = val;
25 next[e] = first[x];
26 first[x] = e++;
27
28 v[e] = x;
29 cost[e] = 0;
30 next[e] = first[y];
31 first[y] = e++;
32 }
33 int SAP() {
34 int pre[MAXN], cur[MAXN], dis[MAXN], gap[MAXN];
35 int flow = 0;
36 int aug = oo;
37 int x, y;
38 bool flag;
39 for (int i = 0; i < n; i++) {
40 cur[i] = first[i];
41 gap[i] = dis[i] = 0;
42 }
43 gap[src] = n;
44 x = pre[src] = src;
45 while (dis[src] < n) {
46 flag = false;
47 for (int &j = cur[x]; j != -1; j = next[j]) {
48 y = v[j];
49 if (cost[j] > 0 && dis[x] == dis[y] + 1) {
50 flag = true;
51 aug = min(aug, cost[j]);
52 pre[y] = x;
53 x = y;
54 if (x == des) {
55 flow += aug;
56 while (x != src) {
57 x = pre[x];
58 cost[cur[x]] -= aug;
59 cost[cur[x] ^ 1] += aug;
60 }
61 aug = oo;
62 }
63 break;
64 }
65 }
66 if (flag) {
67 continue;
68 }
69 int tmp = n;
70 for (int j = first[x]; j != -1; j = next[j]) {
71 y = v[j];
72 if (cost[j] > 0 && dis[y] < tmp) {
73 tmp = dis[y];
74 cur[x] = j;
75 }
76 }
77 if ((--gap[dis[x]]) == 0) {
78 break;
79 }
80 gap[dis[x] = tmp + 1]++;
81 x = pre[x];
82 }
83 return flow;
84 }
85 void out(vector<int> res) {
86 int i;
87 sort(res.begin(), res.end());
88 res.resize(unique(res.begin(), res.end()) - res.begin());
89 for (i = 0; i < (int) res.size(); i++) {
90 if (i) {
91 putchar(' ');
92 }
93 printf("%d", res[i]);
94 }
95 putchar('\n');
96 }
97 void dfs(int x) {
98 int i;
99 vis[x] = true;
100 if (x == des) {
101 flag = false;
102 }
103 for (i = first[x]; i != -1; i = next[i]) {
104 if (!vis[v[i]] && cost[i] > 0) {
105 dfs(v[i]);
106 }
107 }
108 }
109 int main() {
110 int m;
111 int i, j, k;
112 int len;
113 int ans;
114 while (~scanf("%d%d", &m, &n)) {
115 src = 0;
116 des = n + m + 1;
117 e = 0;
118 memset(first, -1, sizeof(first));
119 gets(str);
120 for (i = 1; i <= m; i++) {
121 g[i].clear();
122 gets(str);
123 len = strlen(str);
124 for (j = 0; j < len; j++) {
125 for (; j < len && str[j] == ' '; j++)
126 ;
127 if (j < len) {
128 sscanf(str + j, "%d", &k);
129 g[i].push_back(k);
130 }
131 for (; j < len && isdigit(str[j]); j++)
132 ;
133 }
134 }
135 for (i = 1; i <= n; i++) {
136 scanf("%d", &a[i]);
137 addEdge(m + i, des, a[i]);
138 }
139 ans = 0;
140 for (i = 1; i <= m; i++) {
141 addEdge(src, i, g[i][0]);
142 ans += g[i][0];
143 for (j = 1; j < (int) g[i].size(); j++) {
144 addEdge(i, m + g[i][j], oo);
145 }
146 }
147 n = des + 1;
148 ans -= SAP();
149 test.clear();
150 app.clear();
151 for (i = first[src]; i != -1; i = next[i]) {
152 k = v[i];
153 flag = true;
154 memset(vis, false, sizeof(vis));
155 dfs(k);
156 if (flag) {
157 test.push_back(k);
158 for (j = 1; j < (int) g[k].size(); j++) {
159 app.push_back(g[k][j]);
160 }
161 }
162 }
163 out(test);
164 out(app);
165 printf("%d\n", ans);
166 }
167 return 0;
168 }
(3)最小路径覆盖问题:有向无环图最小路径覆盖。
思路:
1。当原图没有边,增加一条边,就增加一个匹配,显然少了一条路径。
2。当已经有了一个匹配,增加一条边,不会增加匹配数,路径数需增加1。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXN 1010
5 #define MAXM 100010
6 using namespace std;
7 int first[MAXN], v[MAXM], next[MAXM], e;
8 int cx[MAXN], cy[MAXN];
9 bool vis[MAXN];
10 vector<int> res;
11 inline void addEdge(int x, int y) {
12 next[e] = first[x];
13 v[e] = y;
14 first[x] = e++;
15 }
16 int path(int x) {
17 int i;
18 int y;
19 for (i = first[x]; i != -1; i = next[i]) {
20 y = v[i];
21 if (!vis[y]) {
22 vis[y] = true;
23 if (cy[y] == -1 || path(cy[y])) {
24 cx[x] = y;
25 cy[y] = x;
26 return 1;
27 }
28 }
29 }
30 return 0;
31 }
32 int Match(int n) {
33 int i;
34 int ans = 0;
35 memset(cx, -1, sizeof(cx));
36 memset(cy, -1, sizeof(cy));
37 for (i = 1; i <= n; i++) {
38 memset(vis, false, sizeof(vis));
39 ans += path(i);
40 }
41 return n - ans;
42 }
43 void dfs(int x) {
44 vis[x] = true;
45 res.push_back(x);
46 if (cx[x] != -1) {
47 dfs(cx[x]);
48 }
49 }
50 int main() {
51 int n, m;
52 int i, j;
53 int x, y;
54 int ans;
55 while (~scanf("%d%d", &n, &m)) {
56 e = 0;
57 memset(first, -1, sizeof(first));
58 for (i = 0; i < m; i++) {
59 scanf("%d%d", &x, &y);
60 addEdge(x, y);
61 }
62 ans = Match(n);
63 memset(vis, false, sizeof(vis));
64 for (i = 1; i <= n; i++) {
65 if (!vis[i]) {
66 res.clear();
67 dfs(i);
68 for (j = 0; j < (int) res.size() - 1; j++) {
69 printf("%d ", res[j]);
70 }
71 printf("%d\n", res[j]);
72 }
73 }
74 printf("%d\n", ans);
75 }
76 return 0;
77 }
(4)魔术球问题:有向无环图最小路径覆盖。
思路:
1。若x+y为平方数,则x,y'连一条有向边。
2。递增答案,同时增加新的边,更新二分图的交错路。
3。最小路径覆盖=顶点数-二分图最大匹配。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXN 2010
5 #define MAXM 200010
6 using namespace std;
7 int first[MAXN], next[MAXM], v[MAXM], e;
8 int cx[MAXN], cy[MAXN];
9 int nx[MAXN];
10 bool vis[MAXN];
11 bool sqr[MAXM];
12 vector<int> res;
13 void init() {
14 int i;
15 memset(sqr, false, sizeof(sqr));
16 for (i = 1; i * i < MAXM; i++) {
17 sqr[i * i] = true;
18 }
19 }
20 inline void addEdge(int x, int y) {
21 v[e] = y;
22 next[e] = first[x];
23 first[x] = e++;
24 }
25 int path(int x) {
26 int i;
27 int y;
28 for (i = first[x]; i != -1; i = next[i]) {
29 y = v[i];
30 if (!vis[y]) {
31 vis[y] = true;
32 if (cy[y] == -1 || path(cy[y])) {
33 cx[x] = y;
34 cy[y] = x;
35 return 1;
36 }
37 }
38 }
39 return 0;
40 }
41 int cal(int n) {
42 int i;
43 int ans;
44 for (i = 1; i < n; i++) {
45 if (sqr[i + n]) {
46 addEdge(i, n);
47 }
48 }
49 ans = 0;
50 for (i = 1; i <= n; i++) {
51 if (cx[i] == -1) {
52 memset(vis, false, sizeof(vis));
53 ans += path(i);
54 } else {
55 ans++;
56 }
57 }
58 return n - ans;
59 }
60 void dfs(int x) {
61 vis[x] = true;
62 res.push_back(x);
63 if (nx[x] != -1) {
64 dfs(nx[x]);
65 }
66 }
67 int main() {
68 int n;
69 int i, j;
70 init();
71 while (~scanf("%d", &n)) {
72 e = 0;
73 memset(first, -1, sizeof(first));
74 memset(cx, -1, sizeof(cx));
75 memset(cy, -1, sizeof(cy));
76 for (i = 1; cal(i) <= n; i++) {
77 memcpy(nx, cx, sizeof(nx));
78 }
79 n = i - 1;
80 printf("%d\n", n);
81 memset(vis, false, sizeof(vis));
82 for (i = 1; i <= n; i++) {
83 if (!vis[i]) {
84 res.clear();
85 dfs(i);
86 for (j = 0; j < (int) res.size() - 1; j++) {
87 printf("%d ", res[j]);
88 }
89 printf("%d\n", res[j]);
90 }
91 }
92 }
93 return 0;
94 }
(5)圆桌问题:二分图多重匹配。
思路:
1。从源点向每个单位连边,流量为单位人数。
2。从每个圆桌向汇点连边,流量为圆桌人数。
3。每个单位向每个圆桌都连边,流量为1。
4。求最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXN 1010
5 #define MAXM 1000010
6 #define oo 0x7FFFFFFF
7 using namespace std;
8 int first[MAXN], next[MAXM], v[MAXM], cost[MAXM], e;
9 int n;
10 int src, des;
11 inline void addEdge(int x, int y, int val) {
12 v[e] = y;
13 cost[e] = val;
14 next[e] = first[x];
15 first[x] = e++;
16
17 v[e] = x;
18 cost[e] = 0;
19 next[e] = first[y];
20 first[y] = e++;
21 }
22 int SAP() {
23 int pre[MAXN], cur[MAXN], dis[MAXN], gap[MAXN];
24 int flow = 0;
25 int aug = oo;
26 int x, y;
27 bool flag;
28 for (int i = 0; i < n; i++) {
29 cur[i] = first[i];
30 gap[i] = dis[i] = 0;
31 }
32 gap[src] = n;
33 x = pre[src] = src;
34 while (dis[src] < n) {
35 flag = false;
36 for (int &j = cur[x]; j != -1; j = next[j]) {
37 y = v[j];
38 if (cost[j] > 0 && dis[x] == dis[y] + 1) {
39 flag = true;
40 aug = min(aug, cost[j]);
41 pre[y] = x;
42 x = y;
43 if (x == des) {
44 flow += aug;
45 while (x != src) {
46 x = pre[x];
47 cost[cur[x]] -= aug;
48 cost[cur[x] ^ 1] += aug;
49 }
50 aug = oo;
51 }
52 break;
53 }
54 }
55 if (flag) {
56 continue;
57 }
58 int tmp = n;
59 for (int j = first[x]; j != -1; j = next[j]) {
60 y = v[j];
61 if (cost[j] > 0 && dis[y] < tmp) {
62 tmp = dis[y];
63 cur[x] = j;
64 }
65 }
66 if ((--gap[dis[x]]) == 0) {
67 break;
68 }
69 gap[dis[x] = tmp + 1]++;
70 x = pre[x];
71 }
72 return flow;
73 }
74 int main() {
75 int m;
76 int i, j;
77 int tot;
78 vector<int> res[MAXN];
79 scanf("%d%d", &m, &n);
80 src = 0;
81 des = n + m + 1;
82 e = 0;
83 memset(first, -1, sizeof(first));
84 tot = 0;
85 for (i = 1; i <= m; i++) {
86 scanf("%d", &j);
87 addEdge(src, i, j);
88 tot += j;
89 }
90 for (i = 1; i <= n; i++) {
91 scanf("%d", &j);
92 addEdge(m + i, des, j);
93 }
94 for (i = 1; i <= m; i++) {
95 for (j = m + 1; j <= n + m; j++) {
96 addEdge(i, j, 1);
97 }
98 }
99 n = des + 1;
100 for (i = 0; i < MAXN; i++) {
101 res[i].clear();
102 }
103 if (tot == SAP()) {
104 puts("1");
105 for (i = first[src]; i != -1; i = next[i]) {
106 for (j = first[v[i]]; j != -1; j = next[j]) {
107 if (cost[j] == 0) {
108 res[v[i]].push_back(v[j] - m);
109 }
110 }
111 }
112 for (i = 1; i <= m; i++) {
113 for (j = 0; j < (int) res[i].size() - 1; j++) {
114 printf("%d ", res[i][j]);
115 }
116 printf("%d\n", res[i][j]);
117 }
118 } else {
119 puts("0");
120 }
121 return 0;
122 }
(6)最长递增子序列问题:最多不相交路径。
思路:
1。对于第一问:dp即可。
2。每个点取的次数有要求,所以需要拆点,限制流量。
3。控制最长递增子序列,要从dp转移来连边。
题目描述有错:应是最长非降子序列,而不是严格递增的。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXN 10010
5 #define MAXM 1000010
6 #define oo 123456789
7 using namespace std;
8 int first[MAXN], next[MAXM], v[MAXM], cost[MAXM], e;
9 int n;
10 int src, des;
11
12 int dp[MAXN];
13 int arr[MAXN];
14 inline void addEdge(int x, int y, int val) {
15 v[e] = y;
16 cost[e] = val;
17 next[e] = first[x];
18 first[x] = e++;
19
20 v[e] = x;
21 cost[e] = 0;
22 next[e] = first[y];
23 first[y] = e++;
24 }
25 int SAP() {
26 int pre[MAXN], cur[MAXN], dis[MAXN], gap[MAXN];
27 int flow = 0;
28 int aug = oo;
29 int x, y;
30 bool flag;
31 for (int i = 0; i < n; i++) {
32 cur[i] = first[i];
33 gap[i] = dis[i] = 0;
34 }
35 gap[src] = n;
36 x = pre[src] = src;
37 while (dis[src] < n) {
38 flag = false;
39 for (int &j = cur[x]; j != -1; j = next[j]) {
40 y = v[j];
41 if (cost[j] > 0 && dis[x] == dis[y] + 1) {
42 flag = true;
43 aug = min(aug, cost[j]);
44 pre[y] = x;
45 x = y;
46 if (x == des) {
47 flow = min(flow + aug, oo);
48 while (x != src) {
49 x = pre[x];
50 cost[cur[x]] -= aug;
51 cost[cur[x] ^ 1] += aug;
52 }
53 aug = oo;
54 }
55 break;
56 }
57 }
58 if (flag) {
59 continue;
60 }
61 int tmp = n;
62 for (int j = first[x]; j != -1; j = next[j]) {
63 y = v[j];
64 if (cost[j] > 0 && dis[y] < tmp) {
65 tmp = dis[y];
66 cur[x] = j;
67 }
68 }
69 if ((--gap[dis[x]]) == 0) {
70 break;
71 }
72 gap[dis[x] = tmp + 1]++;
73 x = pre[x];
74 }
75 return flow;
76 }
77 int getIndex(int x) {
78 return 2 * x - 1;
79 }
80 void cal(int len, int val) {
81 int i, j;
82 int tmp;
83 int ans;
84 e = 0;
85 memset(first, -1, sizeof(first));
86 src = 0;
87 des = 2 * n + 1;
88 for (i = 1; i <= n; i++) {
89 if (i == 1 || i == n) {
90 addEdge(getIndex(i), getIndex(i) + 1, val);
91 } else {
92 addEdge(getIndex(i), getIndex(i) + 1, 1);
93 }
94 }
95 for (i = 1; i <= n; i++) {
96 if (dp[i] == 1) {
97 addEdge(src, getIndex(i), oo);
98 }
99 if (dp[i] == len) {
100 addEdge(getIndex(i) + 1, des, oo);
101 }
102 }
103 for (i = 1; i <= n; i++) {
104 for (j = 1; j < i; j++) {
105 if (arr[i] >= arr[j] && dp[i] == dp[j] + 1) {
106 addEdge(getIndex(j) + 1, getIndex(i), 1);
107 }
108 }
109 }
110 tmp = n;
111 n = des + 1;
112 ans = SAP();
113 n = tmp;
114 if (ans == oo) {
115 ans = n;
116 }
117 printf("%d\n", ans);
118 }
119 int main() {
120 int i, j;
121 int ans;
122 while (~scanf("%d", &n)) {
123 for (i = 1; i <= n; i++) {
124 scanf("%d", &arr[i]);
125 dp[i] = 1;
126 }
127 for (i = 1; i <= n; i++) {
128 for (j = 1; j < i; j++) {
129 if (arr[i] >= arr[j]) {
130 dp[i] = max(dp[i], dp[j] + 1);
131 }
132 }
133 }
134 ans = 0;
135 for (i = 1; i <= n; i++) {
136 ans = max(ans, dp[i]);
137 }
138 printf("%d\n", ans);
139 cal(ans, 1);
140 cal(ans, oo);
141 }
142 return 0;
143 }
(7)试题库问题:二分图多重匹配。
思路:
1。从源点向每道试题连边,流量为1。
2。从每个类型向汇点连边,流量为每个类型所需要的题数。
3。每个试题向所属的类型都连边,流量为1。
4。求最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXN 1010
5 #define MAXM 1000010
6 #define oo 0x7FFFFFFF
7 using namespace std;
8 int first[MAXN], next[MAXM], v[MAXM], cost[MAXM], e;
9 int n;
10 int src, des;
11 inline void addEdge(int x, int y, int val) {
12 v[e] = y;
13 cost[e] = val;
14 next[e] = first[x];
15 first[x] = e++;
16
17 v[e] = x;
18 cost[e] = 0;
19 next[e] = first[y];
20 first[y] = e++;
21 }
22 int SAP() {
23 int pre[MAXN], cur[MAXN], dis[MAXN], gap[MAXN];
24 int flow = 0;
25 int aug = oo;
26 int x, y;
27 bool flag;
28 for (int i = 0; i < n; i++) {
29 cur[i] = first[i];
30 gap[i] = dis[i] = 0;
31 }
32 gap[src] = n;
33 x = pre[src] = src;
34 while (dis[src] < n) {
35 flag = false;
36 for (int &j = cur[x]; j != -1; j = next[j]) {
37 y = v[j];
38 if (cost[j] > 0 && dis[x] == dis[y] + 1) {
39 flag = true;
40 aug = min(aug, cost[j]);
41 pre[y] = x;
42 x = y;
43 if (x == des) {
44 flow += aug;
45 while (x != src) {
46 x = pre[x];
47 cost[cur[x]] -= aug;
48 cost[cur[x] ^ 1] += aug;
49 }
50 aug = oo;
51 }
52 break;
53 }
54 }
55 if (flag) {
56 continue;
57 }
58 int tmp = n;
59 for (int j = first[x]; j != -1; j = next[j]) {
60 y = v[j];
61 if (cost[j] > 0 && dis[y] < tmp) {
62 tmp = dis[y];
63 cur[x] = j;
64 }
65 }
66 if ((--gap[dis[x]]) == 0) {
67 break;
68 }
69 gap[dis[x] = tmp + 1]++;
70 x = pre[x];
71 }
72 return flow;
73 }
74 int main() {
75 int m;
76 int i, j, k;
77 int tot;
78 vector<int> res[MAXN];
79 while (~scanf("%d%d", &m, &n)) {
80 tot = 0;
81 src = 0;
82 des = m + n + 1;
83 e = 0;
84 memset(first, -1, sizeof(first));
85 for (i = 1; i <= m; i++) {
86 scanf("%d", &j);
87 addEdge(n + i, des, j);
88 tot += j;
89 }
90 for (i = 1; i <= n; i++) {
91 scanf("%d", &j);
92 addEdge(src, i, 1);
93 while (j--) {
94 scanf("%d", &k);
95 addEdge(i, n + k, 1);
96 }
97 }
98 k = n;
99 n = des + 1;
100 if (SAP() == tot) {
101 for (i = 0; i < MAXN; i++) {
102 res[i].clear();
103 }
104 for (i = first[src]; i != -1; i = next[i]) {
105 for (j = first[v[i]]; j != -1; j = next[j]) {
106 if (cost[j] == 0) {
107 if (v[j] > k)
108 res[v[j] - k].push_back(v[i]);
109 }
110 }
111 }
112 for (i = 1; i <= m; i++) {
113 printf("%d:", i);
114 for (j = 0; j < (int) res[i].size(); j++) {
115 printf(" %d", res[i][j]);
116 }
117 putchar('\n');
118 }
119 } else {
120 puts("No Solution!");
121 }
122 }
123 return 0;
124 }
(9)方格取数问题:二分图点权最大独立集。
思路:
1。方格黑白染色。源向黑格连边,流量为点权;白格向汇连边,流量为点权;黑格与相邻的白格连边,流量为oo。因此,最小割是简单割,即最小权覆盖集。
2。由于独立集与覆盖集是互补的,所以最小权覆盖集+最大权独立集=总权值。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXL 210
5 #define MAXN 100010
6 #define MAXM 1000010
7 #define oo 123456789
8 using namespace std;
9 int first[MAXN], next[MAXM], v[MAXM], cost[MAXM], e;
10 int n, m;
11 int src, des;
12 int arr[MAXL][MAXL];
13 int go[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
14 inline void addEdge(int x, int y, int val) {
15 v[e] = y;
16 cost[e] = val;
17 next[e] = first[x];
18 first[x] = e++;
19
20 v[e] = x;
21 cost[e] = 0;
22 next[e] = first[y];
23 first[y] = e++;
24 }
25 int SAP() {
26 int pre[MAXN], cur[MAXN], dis[MAXN], gap[MAXN];
27 int flow = 0;
28 int aug = oo;
29 int x, y;
30 bool flag;
31 for (int i = 0; i < n; i++) {
32 cur[i] = first[i];
33 gap[i] = dis[i] = 0;
34 }
35 gap[src] = n;
36 x = pre[src] = src;
37 while (dis[src] < n) {
38 flag = false;
39 for (int &j = cur[x]; j != -1; j = next[j]) {
40 y = v[j];
41 if (cost[j] > 0 && dis[x] == dis[y] + 1) {
42 flag = true;
43 aug = min(aug, cost[j]);
44 pre[y] = x;
45 x = y;
46 if (x == des) {
47 flow += aug;
48 while (x != src) {
49 x = pre[x];
50 cost[cur[x]] -= aug;
51 cost[cur[x] ^ 1] += aug;
52 }
53 aug = oo;
54 }
55 break;
56 }
57 }
58 if (flag) {
59 continue;
60 }
61 int tmp = n;
62 for (int j = first[x]; j != -1; j = next[j]) {
63 y = v[j];
64 if (cost[j] > 0 && dis[y] < tmp) {
65 tmp = dis[y];
66 cur[x] = j;
67 }
68 }
69 if ((--gap[dis[x]]) == 0) {
70 break;
71 }
72 gap[dis[x] = tmp + 1]++;
73 x = pre[x];
74 }
75 return flow;
76 }
77 int getIndex(int x, int y) {
78 return (x - 1) * m + y;
79 }
80 int main() {
81 int i, j, k;
82 int x, y;
83 int ans;
84 while (~scanf("%d%d", &n, &m)) {
85 src = 0;
86 des = m * n + 1;
87 e = 0;
88 memset(first, -1, sizeof(first));
89 ans = 0;
90 for (i = 1; i <= n; i++) {
91 for (j = 1; j <= m; j++) {
92 scanf("%d", &arr[i][j]);
93 ans += arr[i][j];
94 if ((i + j) & 1) {
95 addEdge(src, getIndex(i, j), arr[i][j]);
96 for (k = 0; k < 4; k++) {
97 x = i + go[k][0];
98 y = j + go[k][1];
99 if (x > 0 && x <= n && y > 0 && y <= m) {
100 addEdge(getIndex(i, j), getIndex(x, y), oo);
101 }
102 }
103 } else {
104 addEdge(getIndex(i, j), des, arr[i][j]);
105 }
106 }
107 }
108 n = des + 1;
109 ans = ans - SAP();
110 printf("%d\n", ans);
111 }
112 return 0;
113 }
(10)餐巾计划问题:线性规划网络优化。
思路:
1。由于第i天的新餐巾可以由i-m天的脏餐巾洗来,因此考虑按天数建模。
2。每天的餐巾有新旧之分,则考虑拆点,每天的新餐巾数量和每天的旧餐巾数量。
3。新餐巾可以直接买来。
4。新餐巾可以由前些天的旧餐巾洗来。
5。旧餐巾可以由上一天的旧餐巾积累而来。
6。求最小费用最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #include<algorithm>
5 #define oo 123456
6 #define MAXN 100010
7 #define MAXM 1000010
8 using namespace std;
9 int V, e;
10 int src, des;
11 int lk[MAXN], father[MAXN];
12 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
13 int dis[MAXN];
14 bool inq[MAXN];
15
16 int arr[MAXN];
17 void addEdge(int x, int y, int f, int c) {
18 v[e] = y;
19 flow[e] = f;
20 cost[e] = c;
21 next[e] = first[x];
22 first[x] = e++;
23
24 v[e] = x;
25 flow[e] = 0;
26 cost[e] = -c;
27 next[e] = first[y];
28 first[y] = e++;
29 }
30 bool SPFA() {
31 int i, u;
32 deque<int> q;
33 memset(inq, false, sizeof(inq));
34 for (i = 0; i <= V; i++) {
35 dis[i] = oo;
36 }
37 dis[src] = 0;
38 q.push_back(src);
39 inq[src] = true;
40 while (!q.empty()) {
41 u = q.front();
42 q.pop_front();
43 inq[u] = false;
44 for (i = first[u]; i != -1; i = next[i]) {
45 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
46 dis[v[i]] = dis[u] + cost[i];
47 father[v[i]] = u;
48 lk[v[i]] = i;
49 if (!inq[v[i]]) {
50 inq[v[i]] = true;
51 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
52 q.push_front(v[i]);
53 } else {
54 q.push_back(v[i]);
55 }
56 }
57 }
58 }
59 }
60 return dis[des] != oo;
61 }
62 int MinCostMaxFlow() {
63 int u;
64 int ans;
65 int tmp;
66 for (ans = 0; SPFA();) {
67 tmp = oo;
68 for (u = des; u; u = father[u]) {
69 tmp = min(tmp, flow[lk[u]]);
70 }
71 for (u = des; u; u = father[u]) {
72 flow[lk[u]] -= tmp;
73 flow[lk[u] ^ 1] += tmp;
74 }
75 ans += tmp * dis[des];
76 }
77 return ans;
78 }
79 int main() {
80 int i;
81 int n;
82 int buy, fastCost, slowCost, slowDay, fastDay;
83 while (~scanf("%d%d%d%d%d%d", &n, &buy, &fastDay, &fastCost, &slowDay,
84 &slowCost)) {
85 e = 0;
86 src = 0;
87 des = n + n + 1;
88 V = des;
89 memset(first, -1, sizeof(first));
90 for (i = 1; i <= n; i++) {
91 scanf("%d", &arr[i]);
92 addEdge(2 * i, des, arr[i], 0);
93 addEdge(src, 2 * i, arr[i], buy);
94 addEdge(src, 2 * i - 1, arr[i], 0);
95 }
96 for (i = 2; i <= n; i++) {
97 addEdge(2 * (i - 1) - 1, 2 * i - 1, oo, 0);
98 }
99 for (i = 1; i <= n; i++) {
100 if (i + fastDay <= n) {
101 addEdge(2 * i - 1, 2 * (i + fastDay), oo, fastCost);
102 }
103 if (i + slowDay <= n) {
104 addEdge(2 * i - 1, 2 * (i + slowDay), oo, slowCost);
105 }
106 }
107 for (i = 1; i <= n; i++) {
108 addEdge(src, i, oo, buy);
109 }
110 printf("%d\n", MinCostMaxFlow());
111 }
112 return 0;
113 }
(11)航空路线问题:最长不相交路径。
思路:
1。从东->西,再从西->东。相当于从东->西两条不相交的路径。
2。要求路径最长,则考虑最大费用最大流。
View Code
1 #include<iostream>
2 #include<algorithm>
3 #include<string>
4 #include<cstring>
5 #include<queue>
6 #include<vector>
7 #include<map>
8 #define oo 0x7FFFFFFF
9 #define MAXL 110
10 #define MAXN 10010
11 #define MAXM 1000010
12 using namespace std;
13 int V, n, m, e;
14 int src, des;
15 int lk[MAXN], father[MAXN];
16 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
17 int dis[MAXN];
18 bool inq[MAXN];
19
20 vector<string> city;
21 map<string, int> mymap;
22 vector<int> res;
23 bool g[MAXL][MAXL];
24 bool vis[MAXN];
25 void addEdge(int x, int y, int f, int c) {
26 v[e] = y;
27 flow[e] = f;
28 cost[e] = c;
29 next[e] = first[x];
30 first[x] = e++;
31
32 v[e] = x;
33 flow[e] = 0;
34 cost[e] = -c;
35 next[e] = first[y];
36 first[y] = e++;
37 }
38 bool SPFA() {
39 int i, u;
40 deque<int> q;
41 memset(inq, false, sizeof(inq));
42 for (i = 0; i <= V; i++) {
43 dis[i] = oo;
44 }
45 dis[src] = 0;
46 q.push_back(src);
47 inq[src] = true;
48 while (!q.empty()) {
49 u = q.front();
50 q.pop_front();
51 inq[u] = false;
52 for (i = first[u]; i != -1; i = next[i]) {
53 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
54 dis[v[i]] = dis[u] + cost[i];
55 father[v[i]] = u;
56 lk[v[i]] = i;
57 if (!inq[v[i]]) {
58 inq[v[i]] = true;
59 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
60 q.push_front(v[i]);
61 } else {
62 q.push_back(v[i]);
63 }
64 }
65 }
66 }
67 }
68 return dis[des] != oo;
69 }
70 int MinCostMaxFlow(int tot) {
71 int u;
72 int ans;
73 int tmp;
74 int flux;
75 for (ans = flux = 0; SPFA();) {
76 tmp = oo;
77 for (u = des; u; u = father[u]) {
78 tmp = min(tmp, flow[lk[u]]);
79 }
80 for (u = des; u; u = father[u]) {
81 flow[lk[u]] -= tmp;
82 flow[lk[u] ^ 1] += tmp;
83 }
84 ans += tmp * dis[des];
85 flux += tmp;
86 }
87 if (flux != tot) {
88 return oo;
89 } else {
90 return ans;
91 }
92 }
93 void dfs(int x) {
94 vis[x] = true;
95 int i;
96 res.push_back(x >> 1);
97 for (i = first[x]; i != -1; i = next[i]) {
98 if ((i & 1) == 0 && flow[i] == 0 && !vis[v[i]]) {
99 dfs(v[i]);
100 }
101 }
102 }
103 int main() {
104 int i, j;
105 string str1, str2;
106 int ans;
107 bool flag;
108 while (cin >> n >> m) {
109 memset(g, false, sizeof(g));
110 src = 0;
111 des = n + n - 1;
112 V = des;
113 e = 0;
114 memset(first, -1, sizeof(first));
115 city.clear();
116 mymap.clear();
117 addEdge(0, 1, 2, 0);
118 addEdge(des - 1, des, 2, 0);
119 for (i = 2; i < des - 1; i += 2) {
120 addEdge(i, i + 1, 1, 0);
121 }
122 for (i = 0; i < n; i++) {
123 cin >> str1;
124 city.push_back(str1);
125 mymap[str1] = i << 1;
126 }
127 while (m--) {
128 cin >> str1 >> str2;
129 i = mymap[str1];
130 j = mymap[str2];
131 if (i > j) {
132 swap(i, j);
133 }
134 addEdge(i ^ 1, j, 1, -1);
135 g[i / 2][j / 2] = g[j / 2][i / 2] = true;
136 }
137 ans = MinCostMaxFlow(2);
138 if (ans == oo) {
139 if (g[0][n - 1]) {
140 cout << 2 << endl;
141 cout << city[0] << endl;
142 cout << city[n - 1] << endl;
143 cout << city[0] << endl;
144 } else {
145 cout << "No Solution!" << endl;
146 }
147 } else {
148 cout << -ans << endl;
149 memset(vis, false, sizeof(vis));
150 flag = false;
151 cout << city[0] << endl;
152 for (i = first[1]; i != -1; i = next[i]) {
153 if ((i & 1) == 0 && flow[i] == 0 && !vis[v[i]]) {
154 res.clear();
155 dfs(v[i]);
156 if (flag) {
157 reverse(res.begin(), res.end());
158 } else {
159 flag = true;
160 }
161 res.resize(unique(res.begin(), res.end()) - res.begin());
162 for (j = 0; j < (int) res.size(); j++) {
163 cout << city[res[j]] << endl;
164 }
165 }
166 }
167 cout << city[0] << endl;
168 }
169 }
170 return 0;
171 }
(12)软件补丁问题:最小转移代价。
思路:
1。只有20个补丁,很容易想到状态压缩。
2。最多(1<<20)个状态,一个状态到另一个状态间有一个花费。
3。求最短花费,就是最短路了。
题目描述有错:第二个字符串中'-'是f1的,'+'是f2的。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #define oo 0x7FFFFFFF
5 #define MAXN 1<<20
6 #define MAXM 110
7 using namespace std;
8 int dis[MAXN];
9 bool inq[MAXN];
10 struct patch {
11 int b1, b2;
12 int f1, f2;
13 int cost;
14 } p[MAXM];
15 int n, m;
16 void update(char str[], int &x, int &y) {
17 int i;
18 x = y = 0;
19 for (i = 0; str[i]; i++) {
20 if (str[i] == '+') {
21 x |= 1 << i;
22 } else if (str[i] == '-') {
23 y |= 1 << i;
24 }
25 }
26 }
27 void spfa(int src) {
28 int i;
29 int x, y;
30 deque<int> q;
31 memset(inq, false, sizeof(inq));
32 for (i = 0; i < (1 << n); i++) {
33 dis[i] = oo;
34 }
35 dis[src] = 0;
36 q.push_back(src);
37 while (!q.empty()) {
38 x = q.front();
39 q.pop_front();
40 inq[x] = false;
41 for (i = 0; i < m; i++) {
42 if ((x | p[i].b1) == x && (x & p[i].b2) == 0) {
43 y = x & (~p[i].f1);
44 y |= p[i].f2;
45 if (dis[y] > dis[x] + p[i].cost) {
46 dis[y] = dis[x] + p[i].cost;
47 if (!inq[y]) {
48 if (!q.empty() && dis[y] <= dis[q.front()]) {
49 q.push_front(y);
50 } else {
51 q.push_back(y);
52 }
53 inq[y] = true;
54 }
55 }
56 }
57 }
58 }
59 }
60 int main() {
61 int i;
62 char a[MAXM], b[MAXM];
63 while (~scanf("%d%d", &n, &m)) {
64 for (i = 0; i < m; i++) {
65 scanf("%d %s %s", &p[i].cost, a, b);
66 update(a, p[i].b1, p[i].b2);
67 update(b, p[i].f2, p[i].f1);
68 }
69 spfa((1 << n) - 1);
70 if (dis[0] == oo) {
71 puts("0");
72 } else {
73 printf("%d\n", dis[0]);
74 }
75 }
76 return 0;
77 }
(13)星际转移问题:网络判定。
思路:
1。枚举天数。随着天数的增加,不断增加点和边。
2。判最大流是否大于等于K。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #include<vector>
5 #define oo 123456
6 #define MAXN 1010
7 #define MAXM 100010
8 using namespace std;
9 int src;
10 int des;
11 int V;
12 int ans;
13 vector<int> path[MAXN];
14 vector<int> cap;
15 vector<int> ind[MAXN];
16 vector<int> pos[MAXN];
17 int G[MAXN][MAXN];
18 int first[MAXN], next[MAXN], v[MAXN], e;
19 bool vis[MAXN];
20 int BFS() {
21 queue<int> q;
22 int tmp, i, u, v, head, p[MAXN];
23 memset(p, -1, sizeof(p));
24 p[src] = src;
25 q.push(src);
26 while (!q.empty()) {
27 head = q.front();
28 q.pop();
29 for (i = 1; i <= V; i++) {
30 if (G[head][i] > 0 && p[i] == -1) {
31 p[i] = head;
32 q.push(i);
33 }
34 }
35 }
36 if (p[des] == -1)
37 return 0;
38 for (tmp = oo, u = des; p[u] != u;) {
39 v = u;
40 u = p[u];
41 tmp = min(tmp, G[u][v]);
42 }
43 for (u = des; p[u] != u;) {
44 v = u;
45 u = p[u];
46 G[u][v] -= tmp;
47 G[v][u] += tmp;
48 }
49 return tmp;
50 }
51 void EdmondsKarp() {
52 int tmp;
53 for (; (tmp = BFS()); ans += tmp)
54 ;
55 }
56 inline void addEdge(int x, int y) {
57 next[e] = first[x];
58 v[e] = y;
59 first[x] = e++;
60 }
61 bool dfs(int x) {
62 int i;
63 int y;
64 vis[x] = true;
65 for (i = first[x]; i != -1; i = next[i]) {
66 y = v[i];
67 if (!vis[y]) {
68 if (dfs(y)) {
69 return true;
70 }
71 }
72 }
73 return x == des;
74 }
75 int main() {
76 int n, m, k;
77 int i, j, t;
78 int tmp;
79 int day;
80 int tot;
81 while (~scanf("%d%d%d", &n, &m, &k)) {
82 cap.clear();
83 for (i = 0; i < m; i++) {
84 scanf("%d%d", &j, &tmp);
85 cap.push_back(j);
86 while (tmp--) {
87 scanf("%d", &j);
88 j += 3;
89 path[i].push_back(j);
90 }
91 }
92 src = 3;
93 des = 2;
94 e = 0;
95 memset(first, -1, sizeof(first));
96 memset(vis, false, sizeof(vis));
97 for (i = 0; i < m; i++) {
98 for (j = 0; j < (int) path[i].size(); j++) {
99 addEdge(path[i][j], path[i][(j + 1) % path[i].size()]);
100 addEdge(path[i][j + 1] % path[i].size(), path[i][j]);
101 }
102 }
103 if (dfs(src)) {
104 ans = 0;
105 memset(G, 0, sizeof(G));
106 src = 0;
107 des = 1;
108 tot = 2;
109 for (i = 0; i < m; i++) {
110 ind[i].clear();
111 pos[i].clear();
112 ind[i].push_back(tot++);
113 pos[i].push_back(path[i][0]);
114 }
115 for (i = 0; i < m; i++) {
116 if (pos[i][0] == 3) {
117 G[src][ind[i][0]] = oo;
118 } else if (pos[i][0] == 2) {
119 G[ind[i][0]][des] = oo;
120 }
121 }
122 for (day = 1; ans < k; day++) {
123 for (i = 0; i < m; i++) {
124 ind[i].push_back(tot++);
125 pos[i].push_back(path[i][day % (path[i].size())]);
126 G[ind[i][day - 1]][ind[i][day]] = cap[i];
127 }
128 for (i = 0; i < m; i++) {
129 for (j = 0; j < m; j++) {
130 for (t = 0; t <= day; t++) {
131 if (pos[j][t] == pos[i][day]) {
132 G[ind[j][t]][ind[i][day]] = oo;
133 }
134 }
135 }
136 }
137 for (i = 0; i < m; i++) {
138 if (pos[i][day] == 3) {
139 G[src][ind[i][day]] = oo;
140 } else if (pos[i][day] == 2) {
141 G[ind[i][day]][des] = oo;
142 }
143 }
144 V = tot;
145 EdmondsKarp();
146 }
147 printf("%d\n", day - 1);
148 } else {
149 puts("0");
150 }
151 }
152 return 0;
153 }
(14)孤岛营救问题:分层图最短路径。
思路:
对钥匙种类状态压缩。dp[i][j][k]表示在(i,j),钥匙种类为k的最短路。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #define MAXN 11
5 #define oo 123456789
6 using namespace std;
7 int dp[MAXN][MAXN][1 << MAXN];
8 int g[MAXN][MAXN][MAXN][MAXN];
9 int mp[MAXN][MAXN];
10 int go[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
11 struct node {
12 int x;
13 int y;
14 int key;
15 int dis;
16 };
17 int n, m, p;
18 inline bool canMove(int x0, int y0, int x1, int y1, int key) {
19 int tmp = g[x0][y0][x1][y1];
20 if (tmp == -1) {
21 return true;
22 } else if (tmp == 0) {
23 return false;
24 } else if (key & (1 << (tmp - 1))) {
25 return true;
26 } else {
27 return false;
28 }
29 }
30 int bfs() {
31 node head, tmp;
32 queue<node> q;
33 int ans;
34 int i, j, k;
35 for (i = 1; i <= n; i++) {
36 for (j = 1; j <= m; j++) {
37 for (k = 0; k < (1 << p); k++)
38 dp[i][j][k] = oo;
39 }
40 }
41 head.x = head.y = 1;
42 head.key = 0;
43 head.dis = 0;
44 q.push(head);
45 while (!q.empty()) {
46 head = q.front();
47 q.pop();
48 for (i = 0; i < 4; i++) {
49 tmp.x = head.x + go[i][0];
50 tmp.y = head.y + go[i][1];
51 if (tmp.x > 0 && tmp.x <= n && tmp.y > 0 && tmp.y <= m
52 && canMove(head.x, head.y, tmp.x, tmp.y, head.key)) {
53 tmp.dis = head.dis + 1;
54 tmp.key = head.key;
55 tmp.key |= mp[tmp.x][tmp.y];
56 if (tmp.dis < dp[tmp.x][tmp.y][tmp.key]) {
57 dp[tmp.x][tmp.y][tmp.key] = tmp.dis;
58 q.push(tmp);
59 }
60 }
61 }
62 }
63 ans = oo;
64 for (i = 0; i < (1 << p); i++) {
65 ans = min(ans, dp[n][m][i]);
66 }
67 if (ans == oo) {
68 return -1;
69 } else {
70 return ans;
71 }
72 }
73 int main() {
74 int i, j;
75 int x0, y0, x1, y1;
76 while (~scanf("%d%d%d", &n, &m, &p)) {
77 memset(g, -1, sizeof(g));
78 memset(mp, 0, sizeof(mp));
79 scanf("%d", &i);
80 while (i--) {
81 scanf("%d%d%d%d%d", &x0, &y0, &x1, &y1, &j);
82 g[x0][y0][x1][y1] = g[x1][y1][x0][y0] = j;
83 }
84 scanf("%d", &i);
85 while (i--) {
86 scanf("%d%d%d", &x0, &y0, &j);
87 mp[x0][y0] |= 1 << (j - 1);
88 }
89 printf("%d\n", bfs());
90 }
91 return 0;
92 }
(15)汽车加油行驶问题:分层图最短路径。
思路:
dp[i][j][k]表示在(i,j),油量为k的最少花费。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #define MAXN 110
5 #define MAXM 15
6 #define oo 123456789
7 using namespace std;
8 int dp[MAXN][MAXN][MAXM];
9 int n, k, a, b, c;
10 int g[MAXN][MAXN];
11 struct node {
12 int x;
13 int y;
14 int gass;
15 int cost;
16 };
17 int go[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };
18 int spfa() {
19 int i, j, l;
20 int ans;
21 node head, tmp;
22 queue<node> q;
23 for (i = 0; i <= n; i++) {
24 for (j = 0; j <= n; j++) {
25 for (l = 0; l <= k; l++) {
26 dp[i][j][l] = oo;
27 }
28 }
29 }
30 dp[1][1][k] = 0;
31 head.x = head.y = 1;
32 head.gass = k;
33 head.cost = 0;
34 q.push(head);
35 while (!q.empty()) {
36 head = q.front();
37 q.pop();
38 if (head.gass == 0) {
39 continue;
40 }
41 for (i = 0; i < 4; i++) {
42 tmp.x = head.x + go[i][0];
43 tmp.y = head.y + go[i][1];
44 if (tmp.x > 0 && tmp.x <= n && tmp.y > 0 && tmp.y <= n) {
45 tmp.gass = head.gass - 1;
46 tmp.cost = head.cost;
47 if (tmp.x < head.x || tmp.y < head.y) {
48 tmp.cost += b;
49 }
50 if (g[tmp.x][tmp.y]) {
51 tmp.gass = k;
52 tmp.cost += a;
53 }
54 if (tmp.cost < dp[tmp.x][tmp.y][tmp.gass]) {
55 dp[tmp.x][tmp.y][tmp.gass] = tmp.cost;
56 q.push(tmp);
57 }
58 if (!g[tmp.x][tmp.y]) {
59 tmp.gass = k;
60 tmp.cost += a + c;
61 if (tmp.cost < dp[tmp.x][tmp.y][tmp.gass]) {
62 dp[tmp.x][tmp.y][tmp.gass] = tmp.cost;
63 q.push(tmp);
64 }
65 }
66 }
67 }
68 }
69 ans = oo;
70 for (i = 0; i <= k; i++) {
71 ans = min(ans, dp[n][n][i]);
72 }
73 return ans;
74 }
75 int main() {
76 int i, j;
77 while (~scanf("%d%d%d%d%d", &n, &k, &a, &b, &c)) {
78 for (i = 1; i <= n; i++) {
79 for (j = 1; j <= n; j++) {
80 scanf("%d", &g[i][j]);
81 }
82 }
83 printf("%d\n", spfa());
84 }
85 return 0;
86 }
(16)数字梯形问题:最大权不相交路径。
思路:
规则1:拆点,点与点之间流量都为1。
规则2:不拆点,点与点流量为1。
规则3:不拆点,点与点流量为无穷。
添加源点,汇点。求最小费用最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #include<vector>
5 #define oo 0x7FFFFFFF
6 #define MAXN 10010
7 #define MAXM 1000010
8 using namespace std;
9 int V, n, m, e;
10 int src, des;
11 int link[MAXN], father[MAXN];
12 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
13 int dis[MAXN];
14 bool inq[MAXN];
15
16 vector<int> arr[MAXN];
17 vector<int> pos[MAXN];
18 int size;
19 void addEdge(int x, int y, int f, int c) {
20 v[e] = y;
21 flow[e] = f;
22 cost[e] = c;
23 next[e] = first[x];
24 first[x] = e++;
25
26 v[e] = x;
27 flow[e] = 0;
28 cost[e] = -c;
29 next[e] = first[y];
30 first[y] = e++;
31 }
32 bool SPFA() {
33 int i, u;
34 deque<int> q;
35 memset(inq, false, sizeof(inq));
36 for (i = 0; i <= V; i++) {
37 dis[i] = oo;
38 }
39 dis[src] = 0;
40 q.push_back(src);
41 inq[src] = true;
42 while (!q.empty()) {
43 u = q.front();
44 q.pop_front();
45 inq[u] = false;
46 for (i = first[u]; i != -1; i = next[i]) {
47 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
48 dis[v[i]] = dis[u] + cost[i];
49 father[v[i]] = u;
50 link[v[i]] = i;
51 if (!inq[v[i]]) {
52 inq[v[i]] = true;
53 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
54 q.push_front(v[i]);
55 } else {
56 q.push_back(v[i]);
57 }
58 }
59 }
60 }
61 }
62 return dis[des] != oo;
63 }
64 int MinCostMaxFlow() {
65 int u;
66 int ans;
67 int tmp;
68 for (ans = 0; SPFA();) {
69 tmp = oo;
70 for (u = des; u; u = father[u]) {
71 tmp = min(tmp, flow[link[u]]);
72 }
73 for (u = des; u; u = father[u]) {
74 flow[link[u]] -= tmp;
75 flow[link[u] ^ 1] += tmp;
76 }
77 ans += tmp * dis[des];
78 }
79 return ans;
80 }
81 int getIndex(int x) {
82 return 2 * x - 1;
83 }
84 void rule1() {
85 int i, j;
86 e = 0;
87 src = 0;
88 des = 2 * size + 1;
89 V = des;
90 memset(first, -1, sizeof(first));
91 for (i = 1; i <= size; i++) {
92 addEdge(getIndex(i), getIndex(i) + 1, 1, 0);
93 }
94 for (i = 0; i < (int) arr[1].size(); i++) {
95 addEdge(src, getIndex(pos[1][i]), 1, -arr[1][i]);
96 }
97 for (i = 1; i < n; i++) {
98 for (j = 0; j < (int) arr[i].size(); j++) {
99 addEdge(getIndex(pos[i][j]) + 1, getIndex(pos[i + 1][j]), 1,
100 -arr[i + 1][j]);
101 addEdge(getIndex(pos[i][j]) + 1, getIndex(pos[i + 1][j + 1]), 1,
102 -arr[i + 1][j + 1]);
103 }
104 }
105 for (j = 0; j < (int) arr[n].size(); j++) {
106 addEdge(getIndex(pos[n][j]) + 1, des, 1, 0);
107 }
108 printf("%d\n", -MinCostMaxFlow());
109 }
110 void rule2() {
111 int i, j;
112 e = 0;
113 src = 0;
114 des = size + 1;
115 V = des;
116 memset(first, -1, sizeof(first));
117 for (i = 0; i < (int) arr[1].size(); i++) {
118 addEdge(src, pos[1][i], 1, -arr[1][i]);
119 }
120 for (i = 1; i < n; i++) {
121 for (j = 0; j < (int) arr[i].size(); j++) {
122 addEdge(pos[i][j], pos[i + 1][j], 1, -arr[i + 1][j]);
123 addEdge(pos[i][j], pos[i + 1][j + 1], 1, -arr[i + 1][j + 1]);
124 }
125 }
126 for (j = 0; j < (int) arr[n].size(); j++) {
127 addEdge(pos[n][j], des, oo, 0);
128 }
129 printf("%d\n", -MinCostMaxFlow());
130 }
131 void rule3() {
132 int i, j;
133 e = 0;
134 src = 0;
135 des = size + 1;
136 V = des;
137 memset(first, -1, sizeof(first));
138 for (i = 0; i < (int) arr[1].size(); i++) {
139 addEdge(src, pos[1][i], 1, -arr[1][i]);
140 }
141 for (i = 1; i < n; i++) {
142 for (j = 0; j < (int) arr[i].size(); j++) {
143 addEdge(pos[i][j], pos[i + 1][j], oo, -arr[i + 1][j]);
144 addEdge(pos[i][j], pos[i + 1][j + 1], oo, -arr[i + 1][j + 1]);
145 }
146 }
147 for (j = 0; j < (int) arr[n].size(); j++) {
148 addEdge(pos[n][j], des, oo, 0);
149 }
150 printf("%d\n", -MinCostMaxFlow());
151 }
152 int main() {
153 int i, j;
154 int tmp;
155 while (~scanf("%d%d", &m, &n)) {
156 size = 0;
157 for (i = 1; i <= n; i++) {
158 arr[i].clear();
159 pos[i].clear();
160 for (j = 0; j < m + i - 1; j++) {
161 scanf("%d", &tmp);
162 arr[i].push_back(tmp);
163 pos[i].push_back(++size);
164 }
165 }
166 rule1();
167 rule2();
168 rule3();
169 }
170 return 0;
171 }
(17)运输问题:网络费用流量。
思路:
1。最小费用最大流。
2。最大费用最大流,费用乘以-1,求最小费用最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #include<vector>
5 #define oo 0x7FFFFFFF
6 #define MAXL 1010
7 #define MAXN 10010
8 #define MAXM 1000010
9 using namespace std;
10 int V, n, m, e;
11 int src, des;
12 int link[MAXN], father[MAXN];
13 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
14 int dis[MAXN];
15 bool inq[MAXN];
16
17 int a[MAXN];
18 int b[MAXN];
19 int c[MAXL][MAXL];
20 void addEdge(int x, int y, int f, int c) {
21 v[e] = y;
22 flow[e] = f;
23 cost[e] = c;
24 next[e] = first[x];
25 first[x] = e++;
26
27 v[e] = x;
28 flow[e] = 0;
29 cost[e] = -c;
30 next[e] = first[y];
31 first[y] = e++;
32 }
33 bool SPFA() {
34 int i, u;
35 deque<int> q;
36 memset(inq, false, sizeof(inq));
37 for (i = 0; i <= V; i++) {
38 dis[i] = oo;
39 }
40 dis[src] = 0;
41 q.push_back(src);
42 inq[src] = true;
43 while (!q.empty()) {
44 u = q.front();
45 q.pop_front();
46 inq[u] = false;
47 for (i = first[u]; i != -1; i = next[i]) {
48 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
49 dis[v[i]] = dis[u] + cost[i];
50 father[v[i]] = u;
51 link[v[i]] = i;
52 if (!inq[v[i]]) {
53 inq[v[i]] = true;
54 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
55 q.push_front(v[i]);
56 } else {
57 q.push_back(v[i]);
58 }
59 }
60 }
61 }
62 }
63 return dis[des] != oo;
64 }
65 int MinCostMaxFlow() {
66 int u;
67 int ans;
68 int tmp;
69 for (ans = 0; SPFA();) {
70 tmp = oo;
71 for (u = des; u; u = father[u]) {
72 tmp = min(tmp, flow[link[u]]);
73 }
74 for (u = des; u; u = father[u]) {
75 flow[link[u]] -= tmp;
76 flow[link[u] ^ 1] += tmp;
77 }
78 ans += tmp * dis[des];
79 }
80 return ans;
81 }
82 void calMinCostMaxFlow(int flag) {
83 int i, j;
84 src = 0;
85 des = n + m + 1;
86 V = des;
87 e = 0;
88 memset(first, -1, sizeof(first));
89 for (i = 1; i <= m; i++) {
90 addEdge(src, i, a[i], 0);
91 }
92 for (i = 1; i <= n; i++) {
93 addEdge(m + i, des, b[i], 0);
94 }
95 for (i = 1; i <= m; i++) {
96 for (j = 1; j <= n; j++) {
97 addEdge(i, m + j, oo, c[i][j] * flag);
98 }
99 }
100 printf("%d\n", flag * MinCostMaxFlow());
101 }
102 int main() {
103 int i, j;
104 while (~scanf("%d%d", &m, &n)) {
105 for (i = 1; i <= m; i++) {
106 scanf("%d", &a[i]);
107 }
108 for (i = 1; i <= n; i++) {
109 scanf("%d", &b[i]);
110 }
111 for (i = 1; i <= m; i++) {
112 for (j = 1; j <= n; j++) {
113 scanf("%d", &c[i][j]);
114 }
115 }
116 calMinCostMaxFlow(1);
117 calMinCostMaxFlow(-1);
118 }
119 return 0;
120 }
(18)分配问题:二分图最佳匹配。
思路:
同(17)运输问题。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #include<vector>
5 #define oo 0x7FFFFFFF
6 #define MAXL 1010
7 #define MAXN 10010
8 #define MAXM 1000010
9 using namespace std;
10 int V, n, e;
11 int src, des;
12 int link[MAXN], father[MAXN];
13 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
14 int dis[MAXN];
15 bool inq[MAXN];
16
17 int c[MAXL][MAXL];
18 void addEdge(int x, int y, int f, int c) {
19 v[e] = y;
20 flow[e] = f;
21 cost[e] = c;
22 next[e] = first[x];
23 first[x] = e++;
24
25 v[e] = x;
26 flow[e] = 0;
27 cost[e] = -c;
28 next[e] = first[y];
29 first[y] = e++;
30 }
31 bool SPFA() {
32 int i, u;
33 deque<int> q;
34 memset(inq, false, sizeof(inq));
35 for (i = 0; i <= V; i++) {
36 dis[i] = oo;
37 }
38 dis[src] = 0;
39 q.push_back(src);
40 inq[src] = true;
41 while (!q.empty()) {
42 u = q.front();
43 q.pop_front();
44 inq[u] = false;
45 for (i = first[u]; i != -1; i = next[i]) {
46 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
47 dis[v[i]] = dis[u] + cost[i];
48 father[v[i]] = u;
49 link[v[i]] = i;
50 if (!inq[v[i]]) {
51 inq[v[i]] = true;
52 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
53 q.push_front(v[i]);
54 } else {
55 q.push_back(v[i]);
56 }
57 }
58 }
59 }
60 }
61 return dis[des] != oo;
62 }
63 int MinCostMaxFlow() {
64 int u;
65 int ans;
66 int tmp;
67 for (ans = 0; SPFA();) {
68 tmp = oo;
69 for (u = des; u; u = father[u]) {
70 tmp = min(tmp, flow[link[u]]);
71 }
72 for (u = des; u; u = father[u]) {
73 flow[link[u]] -= tmp;
74 flow[link[u] ^ 1] += tmp;
75 }
76 ans += tmp * dis[des];
77 }
78 return ans;
79 }
80 void calMinCostMaxFlow(int flag) {
81 int i, j;
82 src = 0;
83 des = n + n + 1;
84 V = des;
85 e = 0;
86 memset(first, -1, sizeof(first));
87 for (i = 1; i <= n; i++) {
88 for (j = 1; j <= n; j++) {
89 addEdge(i, n + j, oo, c[i][j] * flag);
90 }
91 }
92 for (i = 1; i <= n; i++) {
93 addEdge(src, i, 1, 0);
94 addEdge(n + i, des, 1, 0);
95 }
96 printf("%d\n", flag * MinCostMaxFlow());
97 }
98 int main() {
99 int i, j;
100 while (~scanf("%d", &n)) {
101 for (i = 1; i <= n; i++) {
102 for (j = 1; j <= n; j++) {
103 scanf("%d", &c[i][j]);
104 }
105 }
106 calMinCostMaxFlow(1);
107 calMinCostMaxFlow(-1);
108 }
109 return 0;
110 }
(19)负载平衡问题:最小代价供求。
思路:
1。向相邻的左右两个点连边,流量为oo,费用为1。
2。若一个点库存比平均值多a,则从源向该点连边,流量为a,费用为0。
3。若一个点库存比平均值少a,则从该点向汇连边,流量为a,费用为0。
4。求最小费用最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #define oo 123456
5 #define MAXN 100010
6 #define MAXM 1000010
7 using namespace std;
8 int V, n, m, e;
9 int src, des;
10 int lk[MAXN], father[MAXN];
11 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
12 int dis[MAXN];
13 bool inq[MAXN];
14
15 int arr[MAXN];
16 void addEdge(int x, int y, int f, int c) {
17 v[e] = y;
18 flow[e] = f;
19 cost[e] = c;
20 next[e] = first[x];
21 first[x] = e++;
22
23 v[e] = x;
24 flow[e] = 0;
25 cost[e] = -c;
26 next[e] = first[y];
27 first[y] = e++;
28 }
29 bool SPFA() {
30 int i, u;
31 deque<int> q;
32 memset(inq, false, sizeof(inq));
33 for (i = 0; i <= V; i++) {
34 dis[i] = oo;
35 }
36 dis[src] = 0;
37 q.push_back(src);
38 inq[src] = true;
39 while (!q.empty()) {
40 u = q.front();
41 q.pop_front();
42 inq[u] = false;
43 for (i = first[u]; i != -1; i = next[i]) {
44 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
45 dis[v[i]] = dis[u] + cost[i];
46 father[v[i]] = u;
47 lk[v[i]] = i;
48 if (!inq[v[i]]) {
49 inq[v[i]] = true;
50 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
51 q.push_front(v[i]);
52 } else {
53 q.push_back(v[i]);
54 }
55 }
56 }
57 }
58 }
59 return dis[des] != oo;
60 }
61 int MinCostMaxFlow() {
62 int u;
63 int ans;
64 int tmp;
65 for (ans = 0; SPFA();) {
66 tmp = oo;
67 for (u = des; u; u = father[u]) {
68 tmp = min(tmp, flow[lk[u]]);
69 }
70 for (u = des; u; u = father[u]) {
71 flow[lk[u]] -= tmp;
72 flow[lk[u] ^ 1] += tmp;
73 }
74 ans += tmp * dis[des];
75 }
76 return ans;
77 }
78 int main() {
79 int i, j;
80 int sum;
81 while (~scanf("%d", &n)) {
82 e = 0;
83 src = 0;
84 des = n + 1;
85 V = des;
86 memset(first, -1, sizeof(first));
87 sum = 0;
88 for (i = 1; i <= n; i++) {
89 scanf("%d", &arr[i]);
90 sum += arr[i];
91 }
92 sum /= n;
93 for (i = 1; i <= n; i++) {
94 arr[i] -= sum;
95 if (arr[i] > 0) {
96 addEdge(src, i, arr[i], 0);
97 } else if (arr[i] < 0) {
98 addEdge(i, des, -arr[i], 0);
99 }
100 j = i + 1;
101 if (j > n) {
102 j = 1;
103 }
104 addEdge(i, j, oo, 1);
105 addEdge(j, i, oo, 1);
106 j = i - 1;
107 if (j < 1) {
108 j = n;
109 }
110 addEdge(i, j, oo, 1);
111 addEdge(j, i, oo, 1);
112 }
113 printf("%d\n", MinCostMaxFlow());
114 }
115 return 0;
116 }
(20)深海机器人问题:线性规划网络优化。
思路:
1。每个权值只能取一次,流量设为1。
2。每条路径可以取多次,流量设为oo。
3。最大费用最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #define oo 123456
5 #define MAXN 100010
6 #define MAXM 1000010
7 using namespace std;
8 int V, n, m, e;
9 int src, des;
10 int lk[MAXN], father[MAXN];
11 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
12 int dis[MAXN];
13 bool inq[MAXN];
14
15 void addEdge(int x, int y, int f, int c) {
16 v[e] = y;
17 flow[e] = f;
18 cost[e] = c;
19 next[e] = first[x];
20 first[x] = e++;
21
22 v[e] = x;
23 flow[e] = 0;
24 cost[e] = -c;
25 next[e] = first[y];
26 first[y] = e++;
27 }
28 bool SPFA() {
29 int i, u;
30 deque<int> q;
31 memset(inq, false, sizeof(inq));
32 for (i = 0; i <= V; i++) {
33 dis[i] = oo;
34 }
35 dis[src] = 0;
36 q.push_back(src);
37 inq[src] = true;
38 while (!q.empty()) {
39 u = q.front();
40 q.pop_front();
41 inq[u] = false;
42 for (i = first[u]; i != -1; i = next[i]) {
43 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
44 dis[v[i]] = dis[u] + cost[i];
45 father[v[i]] = u;
46 lk[v[i]] = i;
47 if (!inq[v[i]]) {
48 inq[v[i]] = true;
49 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
50 q.push_front(v[i]);
51 } else {
52 q.push_back(v[i]);
53 }
54 }
55 }
56 }
57 }
58 return dis[des] != oo;
59 }
60 int MinCostMaxFlow() {
61 int u;
62 int ans;
63 int tmp;
64 for (ans = 0; SPFA();) {
65 tmp = oo;
66 for (u = des; u; u = father[u]) {
67 tmp = min(tmp, flow[lk[u]]);
68 }
69 for (u = des; u; u = father[u]) {
70 flow[lk[u]] -= tmp;
71 flow[lk[u] ^ 1] += tmp;
72 }
73 ans += tmp * dis[des];
74 }
75 return ans;
76 }
77 int main() {
78 int a, b;
79 int p, q;
80 int i, j;
81 int x, y, val;
82 while (~scanf("%d%d%d%d", &a, &b, &p, &q)) {
83 e = 0;
84 src = 0;
85 des = (p + 1) * (q + 1) + 1;
86 V = des;
87 memset(first, -1, sizeof(first));
88 for (i = 0; i <= p; i++) {
89 for (j = 1; j <= q; j++) {
90 scanf("%d", &x);
91 addEdge(i * (q + 1) + j, i * (q + 1) + j + 1, 1, -x);
92 addEdge(i * (q + 1) + j, i * (q + 1) + j + 1, oo, 0);
93 }
94 }
95 for (i = 0; i <= q; i++) {
96 for (j = 1; j <= p; j++) {
97 scanf("%d", &x);
98 addEdge((j - 1) * (q + 1) + i + 1, j * (q + 1) + i + 1, 1, -x);
99 addEdge((j - 1) * (q + 1) + i + 1, j * (q + 1) + i + 1, oo, 0);
100 }
101 }
102 while (a--) {
103 scanf("%d%d%d", &val, &y, &x);
104 addEdge(src, y * (q + 1) + x + 1, val, 0);
105 }
106 while (b--) {
107 scanf("%d%d%d", &val, &y, &x);
108 addEdge(y * (q + 1) + x + 1, des, val, 0);
109 }
110 printf("%d\n", -MinCostMaxFlow());
111 }
112 }
(21)最长k可重区间集问题:最大权不相交路径。
思路:
1。对所有端点排序后,离散化。
2。源到1,流量为k,费用为0。最后一个点到汇,流量为oo,费用为0。
3。若有区间[x,y],则x向y连边,流量为1,费用为x-y。
4。最大费用最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 #include<algorithm>
5 #define oo 123456
6 #define MAXN 100010
7 #define MAXM 1000010
8 using namespace std;
9 int V, n, m, e;
10 int src, des;
11 int lk[MAXN], father[MAXN];
12 int first[MAXN], cost[MAXM], flow[MAXM], next[MAXM], v[MAXM];
13 int dis[MAXN];
14 bool inq[MAXN];
15
16 int arr[MAXN];
17 int size;
18 struct point {
19 int x, y;
20 } p[MAXN];
21 void addEdge(int x, int y, int f, int c) {
22 v[e] = y;
23 flow[e] = f;
24 cost[e] = c;
25 next[e] = first[x];
26 first[x] = e++;
27
28 v[e] = x;
29 flow[e] = 0;
30 cost[e] = -c;
31 next[e] = first[y];
32 first[y] = e++;
33 }
34 bool SPFA() {
35 int i, u;
36 deque<int> q;
37 memset(inq, false, sizeof(inq));
38 for (i = 0; i <= V; i++) {
39 dis[i] = oo;
40 }
41 dis[src] = 0;
42 q.push_back(src);
43 inq[src] = true;
44 while (!q.empty()) {
45 u = q.front();
46 q.pop_front();
47 inq[u] = false;
48 for (i = first[u]; i != -1; i = next[i]) {
49 if (flow[i] && dis[v[i]] > dis[u] + cost[i]) {
50 dis[v[i]] = dis[u] + cost[i];
51 father[v[i]] = u;
52 lk[v[i]] = i;
53 if (!inq[v[i]]) {
54 inq[v[i]] = true;
55 if (!q.empty() && dis[v[i]] <= dis[q.front()]) {
56 q.push_front(v[i]);
57 } else {
58 q.push_back(v[i]);
59 }
60 }
61 }
62 }
63 }
64 return dis[des] != oo;
65 }
66 int MinCostMaxFlow() {
67 int u;
68 int ans;
69 int tmp;
70 for (ans = 0; SPFA();) {
71 tmp = oo;
72 for (u = des; u; u = father[u]) {
73 tmp = min(tmp, flow[lk[u]]);
74 }
75 for (u = des; u; u = father[u]) {
76 flow[lk[u]] -= tmp;
77 flow[lk[u] ^ 1] += tmp;
78 }
79 ans += tmp * dis[des];
80 }
81 return ans;
82 }
83 int main() {
84 int i;
85 int m;
86 int x, y;
87 while (~scanf("%d%d", &n, &m)) {
88 e = 0;
89 src = 0;
90 des = n + n + 1;
91 V = des;
92 memset(first, -1, sizeof(first));
93 size = 0;
94 for (i = 0; i < n; i++) {
95 scanf("%d%d", &p[i].x, &p[i].y);
96 arr[size++] = p[i].x;
97 arr[size++] = p[i].y;
98 }
99 sort(arr, arr + size);
100 size = unique(arr, arr + size) - arr;
101 addEdge(src, 1, m, 0);
102 addEdge(size, des, oo, 0);
103 for (i = 2; i <= size; i++) {
104 addEdge(i - 1, i, oo, 0);
105 }
106 for (i = 0; i < n; i++) {
107 x = lower_bound(arr, arr + size, p[i].x) - arr + 1;
108 y = lower_bound(arr, arr + size, p[i].y) - arr + 1;
109 addEdge(x, y, 1, p[i].x - p[i].y);
110 }
111 printf("%d\n", -MinCostMaxFlow());
112 }
113 return 0;
114 }
(24)骑士共存问题:二分图最大独立集。
思路:
1。冲突的位置相互连边。
2.。添加源,汇。
3。求最大流。
View Code
1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #define MAXL 210
5 #define MAXN 100010
6 #define MAXM 1000010
7 #define oo 0x7FFFFFFF
8 using namespace std;
9 int first[MAXN], next[MAXM], v[MAXM], cost[MAXM], e;
10 int n;
11 int src, des;
12 bool flag[MAXL][MAXL];
13 int go[][2] = { { 1, 2 }, { 1, -2 }, { -1, 2 }, { -1, -2 }, { 2, 1 }, { 2, -1 },
14 { -2, 1 }, { -2, -1 } };
15 inline void addEdge(int x, int y, int val) {
16 v[e] = y;
17 cost[e] = val;
18 next[e] = first[x];
19 first[x] = e++;
20
21 v[e] = x;
22 cost[e] = 0;
23 next[e] = first[y];
24 first[y] = e++;
25 }
26 int SAP() {
27 int pre[MAXN], cur[MAXN], dis[MAXN], gap[MAXN];
28 int flow = 0;
29 int aug = oo;
30 int x, y;
31 bool flag;
32 for (int i = 0; i < n; i++) {
33 cur[i] = first[i];
34 gap[i] = dis[i] = 0;
35 }
36 gap[src] = n;
37 x = pre[src] = src;
38 while (dis[src] < n) {
39 flag = false;
40 for (int &j = cur[x]; j != -1; j = next[j]) {
41 y = v[j];
42 if (cost[j] > 0 && dis[x] == dis[y] + 1) {
43 flag = true;
44 aug = min(aug, cost[j]);
45 pre[y] = x;
46 x = y;
47 if (x == des) {
48 flow += aug;
49 while (x != src) {
50 x = pre[x];
51 cost[cur[x]] -= aug;
52 cost[cur[x] ^ 1] += aug;
53 }
54 aug = oo;
55 }
56 break;
57 }
58 }
59 if (flag) {
60 continue;
61 }
62 int tmp = n;
63 for (int j = first[x]; j != -1; j = next[j]) {
64 y = v[j];
65 if (cost[j] > 0 && dis[y] < tmp) {
66 tmp = dis[y];
67 cur[x] = j;
68 }
69 }
70 if ((--gap[dis[x]]) == 0) {
71 break;
72 }
73 gap[dis[x] = tmp + 1]++;
74 x = pre[x];
75 }
76 return flow;
77 }
78 int getIndex(int x, int y) {
79 return (x - 1) * n + y;
80 }
81 int main() {
82 int m;
83 int i, j, k;
84 int x, y;
85 int ans;
86 while (~scanf("%d%d", &n, &m)) {
87 src = 0;
88 des = 2 * n * n + 1;
89 e = 0;
90 memset(first, -1, sizeof(first));
91 memset(flag, false, sizeof(flag));
92 for (i = 0; i < m; i++) {
93 scanf("%d%d", &x, &y);
94 flag[x][y] = true;
95 }
96 for (i = 1; i <= n; i++) {
97 for (j = 1; j <= n; j++) {
98 if (flag[i][j]) {
99 continue;
100 }
101 addEdge(src, getIndex(i, j), 1);
102 addEdge(n * n + getIndex(i, j), des, 1);
103 for (k = 0; k < 8; k++) {
104 x = i + go[k][0];
105 y = j + go[k][1];
106 if (x > 0 && x <= n && y > 0 && y <= n && !flag[x][y]) {
107 addEdge(getIndex(i, j), n * n + getIndex(x, y), 1);
108 }
109 }
110 }
111 }
112 ans = n * n - m;
113 n = des + 1;
114 printf("%d\n", ans - SAP() / 2);
115 }
116 return 0;
117 }
来源:https://www.cnblogs.com/DrunBee/archive/2013/05/08/3053598.html
