《挑战程序设计竞赛》课后练习题解集——2.5 它们其实都是“图”

Posted hs-zlq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《挑战程序设计竞赛》课后练习题解集——2.5 它们其实都是“图”相关的知识,希望对你有一定的参考价值。

2.5 它们其实都是“图”

最短路

 

AOJ 0189  求图上一点,到所有其他点的距离之和最小

Floyd算法

技术图片
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int inf = 1e8;
 5 int d[11][11];
 6 
 7 int main() {
 8     int n;
 9     while (cin >> n) {
10         if (n == 0) break;
11 
12         for (int i = 0; i <= 9; i++)
13             for (int j = 0; j <= 9; j++) d[i][j] = inf;
14         for (int i = 0; i <= 9; i++) d[i][i] = 0;
15 
16         int x, y, z, v = 0;
17         while (n--) {
18             cin >> x >> y >> z;
19             v = max(max(x, y), v);
20             d[x][y] = z;
21             d[y][x] = z;
22         }
23 
24         for (int k = 0; k <= v; k++) {
25             for (int i = 0; i <= v; i++) {
26                 for (int j = 0; j <= v; j++)
27                     d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
28             }
29         }
30 
31         int sum = inf, number;
32         for (int i = 0; i <= v; i++) {
33             int t = 0;
34             for (int j = 0; j <= v; j++) t += d[i][j];
35             if (sum > t) {
36                 sum = t;
37                 number = i;
38             }
39         }
40 
41         cout << number << " " << sum << "
";
42     }
43 }
View Code

 

POJ 2139  同上一题,结果乘100除以n-1

技术图片
 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 
 5 int d[305][305], x[305];
 6 
 7 int main() {
 8     int n, m, num;
 9     for (int i = 1; i <= 300; i++) fill(d[i], d[i] + 305, 0x7f7f7f);
10     for (int i = 1; i <= 300; i++) d[i][i] = 0;
11     cin >> n >> m;
12     for (int i = 0; i < m; i++) {
13         cin >> num;
14         for (int j = 0; j < num; j++) {
15             cin >> x[j];
16             for (int k = 0; k < j; k++) {
17                 d[x[k]][x[j]] = 1;
18                 d[x[j]][x[k]] = 1;
19             }
20         }
21     }
22     for (int i = 1; i <= n; i++) {
23         for (int j = 1; j <= n; j++) {
24             for (int k = 1; k <= n; k++)
25                 d[j][k] = min(d[j][k], d[j][i] + d[i][k]);
26         }
27     }
28     int sum = (int)1e9;
29     for (int i = 1; i <= n; i++) {
30         int t = 0;
31         for (int j = 1; j <= n; j++) t += d[i][j];
32         sum = min(sum, t);
33     }
34     cout << sum * 100 / (n - 1);
35 }
View Code

 

POJ 3259  判断是否有负环

BF算法

技术图片
 1 #include <cstring>
 2 #include <iostream>
 3 using namespace std;
 4 
 5 int d[505];
 6 
 7 struct edge {
 8     int from, to, cost;
 9 };
10 
11 edge es[5205];
12 
13 int main() {
14     int t, n, m, w, x, y, z;
15     cin >> t;
16     while (t--) {
17         cin >> n >> m >> w;
18         for (int i = 0; i < m; i++) {
19             cin >> x >> y >> z;
20             es[2 * i] = {x, y, z};
21             es[2 * i + 1] = {y, x, z};
22         }
23         for (int i = 2 * m; i < 2 * m + w; i++) {
24             cin >> x >> y >> z;
25             es[i] = {x, y, -z};
26         }
27         memset(d, 0, sizeof(d));
28         int flag = 0;
29         for (int i = 1; i <= n; i++) {
30             for (int j = 0; j < 2 * m + w; j++) {
31                 edge e = es[j];
32                 if (d[e.to] > d[e.from] + e.cost) {
33                     d[e.to] = d[e.from] + e.cost;
34                     if (i == n) {
35                         cout << "YES
";
36                         flag = 1;
37                         break;
38                     }
39                 }
40             }
41         }
42         if (flag == 0) cout << "NO
";
43     }
44 }
View Code

 

POJ 3268  求所有点到某一点来回的最短路中的最大值

dij求该点到所有点的最短路,反过来时把所有边反向即可

技术图片
 1 #include <cstring>
 2 #include <iostream>
 3 using namespace std;
 4 
 5 int d[505];
 6 
 7 struct edge {
 8     int from, to, cost;
 9 };
10 
11 edge es[5205];
12 
13 int main() {
14     int t, n, m, w, x, y, z;
15     cin >> t;
16     while (t--) {
17         cin >> n >> m >> w;
18         for (int i = 0; i < m; i++) {
19             cin >> x >> y >> z;
20             es[2 * i] = {x, y, z};
21             es[2 * i + 1] = {y, x, z};
22         }
23         for (int i = 2 * m; i < 2 * m + w; i++) {
24             cin >> x >> y >> z;
25             es[i] = {x, y, -z};
26         }
27         memset(d, 0, sizeof(d));
28         int flag = 0;
29         for (int i = 1; i <= n; i++) {
30             for (int j = 0; j < 2 * m + w; j++) {
31                 edge e = es[j];
32                 if (d[e.to] > d[e.from] + e.cost) {
33                     d[e.to] = d[e.from] + e.cost;
34                     if (i == n) {
35                         cout << "YES
";
36                         flag = 1;
37                         break;
38                     }
39                 }
40             }
41         }
42         if (flag == 0) cout << "NO
";
43     }
44 }
View Code

 

 AOJ 2249  给出一个连通图,每条边具有距离和修建的成本。要删去一些边,使得从起点1出发到其他的点的最短路径不变 而省下的修建成本尽可能多

可以把修建的成本当做第二维费用 跑dij

技术图片
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define pii pair<int, int>
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 
10 const int maxv = 1e4 + 5;
11 const int inf = 0x3f3f3f3f;
12 int V, d[maxv], c[maxv], m, res;
13 struct edge {
14     int to, dis, cost;
15     bool operator>(const edge& o) const {
16         if (dis == o.dis) return cost > o.cost;
17         return dis > o.dis;
18     }
19 };
20 vector<edge> g[maxv];
21 
22 void dij(int s) {
23     priority_queue<edge, vector<edge>, greater<edge> > que;
24     inc(i, 1, V) d[i] = c[i] = inf;
25     d[s] = c[s] = 0;
26     que.push({s, 0, 0});
27     while (!que.empty()) {
28         edge p = que.top();
29         que.pop();
30         int v = p.to;
31         if (d[v] < p.dis || (d[v] == p.dis && c[v] < p.cost)) continue;
32         for (int i = 0; i < g[v].size(); i++) {
33             edge e = g[v][i];
34             if (d[e.to] > d[v] + e.dis ||
35                 (d[e.to] == d[v] + e.dis && c[e.to] > e.cost)) {
36                 d[e.to] = d[v] + e.dis;
37                 c[e.to] = e.cost;
38                 que.push({e.to, d[e.to], c[e.to]});
39             }
40         }
41     }
42 }
43 
44 int u, v, x, y;
45 
46 int main() {
47     while (scanf("%d %d", &V, &m) != EOF && V) {
48         inc(i, 1, V) g[i].clear();
49         inc(i, 1, m) {
50             scanf("%d %d %d %d", &u, &v, &x, &y);
51             g[u].pb({v, x, y});
52             g[v].pb({u, x, y});
53         }
54         dij(1);
55         res = 0;
56         inc(i, 1, V) res += c[i];
57         printf("%d
", res);
58     }
59 }
View Code

 

AOJ 2200  给出一个图,边有陆路和水路。有个快递员要经过一系列点,他有辆船,走水路时必须用船,走陆路时船得留在出发时的起点,要用船时得回去取。求最短距离

好题目。Floyd分别预处理陆路和水路,dp[i][j]表示当前走到第i个城镇,船停在第j号城镇的最段距离。转移时考虑去取船,走水路,下船走陆路这一过程。特别,可以不取船走陆路到下一个地方

(最开始我dp想当然觉得船都是停在目标路径上的节点,其实不然)

技术图片
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 
 6 const int inf = 2e8;
 7 
 8 int n, m, r;
 9 int d1[205][205], d2[205][205];
10 int path[1005], dp[1005][205];
11 
12 void init(int p[][205]) {
13     inc(i, 1, n) inc(j, 1, n) p[i][j] = inf;
14     inc(i, 1, n) p[i][i] = 0;
15 }
16 
17 void floyd(int p[][205]) {
18     inc(k, 1, n) inc(i, 1, n) inc(j, 1, n) p[i][j] =
19         min(p[i][j], p[i][k] + p[k][j]);
20 }
21 
22 int x, y, z;
23 char c;
24 
25 int main() {
26     while (scanf("%d %d", &n, &m) != EOF && n) {
27         init(d1), init(d2);
28         inc(i, 1, m) {
29             scanf("%d %d %d %c", &x, &y, &z, &c);
30             if (c == L)
31                 d1[x][y] = d1[y][x] = min(d1[x][y], z);
32             else
33                 d2[x][y] = d2[y][x] = min(d2[x][y], z);
34         }
35         floyd(d1), floyd(d2);
36 
37         scanf("%d", &r);
38         inc(i, 1, r) scanf("%d", &path[i]);
39 
40         inc(i, 1, n) dp[1][i] = inf;
41         dp[1][path[1]] = 0;
42 
43         inc(i, 2, r) {
44             inc(j, 1, n) {
45                 dp[i][j] = inf;
46                 inc(k, 1, n) {
47                     dp[i][j] = min(dp[i][j], dp[i - 1][k] + d1[path[i - 1]][k] +
48                                                  d2[k][j] + d1[j][path[i]]);
49                     if (k == j)
50                         dp[i][j] = min(dp[i][j],
51                                        dp[i - 1][k] + d1[path[i - 1]][path[i]]);
52                 }
53             }
54         }
55 
56         int res = inf;
57         inc(i, 1, n) res = min(res, dp[r][i]);
58         printf("%d
", res);
59     }
60 }
View Code

 

最小生成树

POJ 1258  给出一个图的邻接矩阵,求最小生成树

技术图片
 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 using namespace std;
 6 
 7 int par[105], rankk[105];
 8 struct edge {
 9     int u, v, cost;
10 } es[10005];
11 
12 bool cmp(const edge &x, const edge &y) { return x.cost < y.cost; }
13 
14 void init(int x) {
15     for (int i = 1; i <= x; i++) {
16         par[i] = i;
17         rankk[i] = 0;
18     }
19 }
20 
21 int find(int x) {
22     if (par[x] == x) return x;
23     return par[x] = find(par[x]);
24 }
25 
26 void unite(int x, int y) {
27     x = find(x);
28     y = find(y);
29     if (x == y) return;
30     if (rankk[x] < rankk[y])
31         par[x] = y;
32     else {
33         par[y] = x;
34         if (rankk[x] == rankk[y]) rankk[x]++;
35     }
36 }
37 
38 bool same(int x, int y) { return find(x) == find(y); }
39 
40 int main() {
41     int n, x, l;
42     while (~scanf("%d", &n)) {
43         init(n);
44         l = 0;
45         memset(es, 0, sizeof(es));
46         for (int i = 1; i <= n; i++) {
47             for (int j = 1; j <= n; j++) {
48                 scanf("%d", &x);
49                 if (i == j) continue;
50                 es[l++] = {i, j, x};
51             }
52         }
53         sort(es, es + l, cmp);
54         int res = 0;
55         for (int i = 0; i < l; i++) {
56             edge e = es[i];
57             if (!same(e.u, e.v)) {
58                 unite(e.u, e.v);
59                 res += e.cost;
60             }
61         }
62         printf("%d
", res);
63     }
64 }
View Code

 

POJ 2377  最大生成树

技术图片
 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <iostream>
 4 using namespace std;
 5 
 6 int par[1001], rankk[1001];
 7 
 8 struct edge {
 9     int u, v, cost;
10 } es[20001];
11 
12 void init(int x) {
13     for (int i = 1; i <= x; i++) {
14         par[i] = i;
15         rankk[i] = 0;
16     }
17 }
18 
19 int find(int x) {
20     if (par[x] == x) return x;
21     return par[x] = find(par[x]);
22 }
23 
24 void unite(int x, int y) {
25     x = find(x);
26     y = find(y);
27     if (x == y) return;
28     if (rankk[y] > rankk[x]) {
29         par[x] = y;
30     } else {
31         par[y] = x;
32         if (rankk[x] == rankk[y]) rankk[x]++;
33     }
34 }
35 
36 bool same(int x, int y) { return find(x) == find(y); }
37 
38 bool cmp(edge x, edge y) { return x.cost > y.cost; }
39 
40 int main() {
41     int n, m, x, y, z, res = 0, sum = 0;
42     cin >> n >> m;
43     init(n);
44     for (int i = 0; i < m; i++) {
45         scanf("%d%d%d", &x, &y, &z);
46         es[i] = {x, y, z};
47     }
48     sort(es, es + m, cmp);
49     for (int i = 0; i < m; i++) {
50         edge e = es[i];
51         if (!same(e.u, e.v)) {
52             unite(e.u, e.v);
53             sum += e.cost;
54             res++;
55         }
56     }
57     if (res == n - 1)
58         printf("%d", sum);
59     else
60         printf("-1");
61 }
View Code

 

AOJ 2224  给出一个图,要求删去一些边,使得删去后 被这些边划分的所有区域是互相联通的。要使删去的边边长和最小
这题难度在于条件转换。题意等价于图删去一些边后没有环。考虑删边后的图 的边长要尽可能的大,即 是由 若干最大生成树组成的森林

技术图片
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define inc(i, l, r) for (int i = l; i <= r; i++)
 5 #define pii pair<double, int>
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 
10 const double inf = 1e9;
11 const int maxv = 1e5 + 5;
12 
13 struct edge {
14     double cost;
15     int u, v;
16     bool operator<(const edge &o) const { return cost > o.cost; }
17 };
18 
19 double x[maxv], y[maxv], tot;
20 int n, m, a, b;
21 
22 int par[maxv];
23 void init() { inc(i, 1, n) par[i] = i; }
24 int find(int x) {
25     if (x == par[x]) return x;
26     return par[x] = find(par[x]);
27 }
28 void unite(int x, int y) {
29     x = find(x), y = find(y);
30     if (x != y) par[x] = y;
31 }
32 bool same(int x, int y) { return find(x) == find(y); }
33 
34 int main() {
35     scanf("%d %d", &n, &m);
36     vector<edge> g(m);
37     inc(i, 1, n) scanf("%lf %lf", &x[i], &y[i]);
38     inc(i, 0, m - 1) {
39         scanf("%d %d", &a, &b);
40         double dis =
41             sqrt((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]));
42         g[i] = {dis, a, b};
43         tot += dis;
44     }
45     sort(g.begin(), g.end());
46     init();
47     inc(i, 0, m - 1) {
48         int u = g[i].u, v = g[i].v;
49         if (!same(u, v)) {
50             tot -= g[i].cost;
51             unite(u, v);
52         }
53     }
54     printf("%.3f", tot);
55 }
View Code

 

POJ 2395  给定一个图,求它的联通子图(含所有点)最大边的最小值

即最小生成树的最大边

技术图片
 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <vector>
 5 using namespace std;
 6 #define inc(i, l, r) for (int i = l; i <= r; i++)
 7 
 8 const int maxv = 2e3 + 5;
 9 
10 int par[maxv], n, m, u, v, x;
11 
12 struct edge {
13     int u, v, cost;
14     bool operator<(const edge& o) const { return cost < o.cost; }
15 } g[10005];
16 
17 void init() { inc(i, 1, n) par[i] = i; }
18 int find(int x) {
19     if (x == par[x]) return x;
20     return par[x] = find(par[x]);
21 }
22 void unite(int x, int y) {
23     x = find(x), y = find(y);
24     if (x != y) par[x] = y;
25 }
26 bool same(int x, int y) { return find(x) == find(y); }
27 
28 int main() {
29     scanf("%d %d", &n, &m);
30     init();
31     inc(i, 0, m - 1) {
32         scanf("%d %d %d", &u, &v, &x);
33         g[i] = edge{u, v, x};
34     }
35     sort(g, g + m);
36     int res = 0;
37     inc(i, 0, m - 1) {
38         int u = g[i].u, v = g[i].v;
39         if (!same(u, v)) {
40             unite(u, v);
41             res = max(res, g[i].cost);
42         }
43     }
44     printf("%d
", res);
45 }
View Code 

 

END

以上是关于《挑战程序设计竞赛》课后练习题解集——2.5 它们其实都是“图”的主要内容,如果未能解决你的问题,请参考以下文章

《挑战程序设计竞赛》课后练习题解集——3.2 常用技巧精选

《挑战程序设计竞赛》课后练习题解集——2.3 记录结果再利用的“动态规划”

挑战程序设计竞赛 2.5 它们其实都是“图”

AOJ 0033 Ball 题解 《挑战程序设计竞赛》

[转] AOJ 0525 Osenbei《挑战程序设计竞赛(第2版)》练习题答案

POJ 1862 Stripies 题解 《挑战程序设计竞赛》