《挑战程序设计竞赛》课后练习题解集——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 }
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 }
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 }
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 }
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 }
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 }
最小生成树
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 }
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 }
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 }
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 }
END
以上是关于《挑战程序设计竞赛》课后练习题解集——2.5 它们其实都是“图”的主要内容,如果未能解决你的问题,请参考以下文章
《挑战程序设计竞赛》课后练习题解集——2.3 记录结果再利用的“动态规划”