当前位置:   article > 正文

0x62 图论-最小生成树

描述 给定一棵n个节点的树,如果树中存在边(a,b)(b,c)(c,d),a,b,c,d互不相同,

A题:走廊泼水节

链接:https://ac.nowcoder.com/acm/contest/1056/A

题目描述

给定一棵N个节点的树,要求增加若干条边,把这棵树扩充为完全图,并满足图的唯一最小生成树仍然是这棵树。

求增加的边的权值总和最小是多少。

输入描述:

  1. 第一行包含整数t,表示共有t组测试数据。
  2. 对于每组测试数据,第一行包含整数N。
  3. 接下来N-1行,每行三个整数X,Y,Z,表示X节点与Y节点之间存在一条边,长度为Z。

输出描述:

  1. 每组数据输出一个整数,表示权值总和最小值。
  2. 每个结果占一行。

示例1

输入

  1. 2
  2. 3
  3. 1 2 2
  4. 1 3 3
  5. 4
  6. 1 2 3
  7. 2 3 4
  8. 3 4 5

输出

  1. 4
  2. 17

备注:

N<=6000,Z<=100

例解释

第一组数据,在 22 和 33 之间修建一条长度为 44 的道路,
使这棵树变成一个完全图,且原来的树依然是这个图的唯一最小生成树.
 

题解

  • 对给定树上的 N1 条边模拟一遍Kruskal
  • 通过边$(x,y) $合并两个并查集
  • xx 集合中的每个点到 y 集合中的每个点
  • 添加一条长度为 $w(x,y)+1 $的边

pic

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<algorithm>
  5. using namespace std;
  6. #define maxn 6010
  7. struct edge{ int u,v,w; }e[maxn];
  8. int t,n,f[6010],s[6010];
  9. long long ans;
  10. bool cmp(edge x,edge y){ return x.w<y.w; }
  11. int find(int x){
  12. if(f[x]!=x) f[x]=find(f[x]);
  13. return f[x];
  14. }
  15. int main(){
  16. scanf("%d",&t);
  17. while(t--){
  18. scanf("%d",&n);
  19. for(int i=1;i<=n;++i){ f[i]=i; s[i]=1; }
  20. for(int i=1;i<n;++i) scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
  21. sort(e+1,e+n,cmp);
  22. ans=0;
  23. for(int fu,fv,i=1;i<n;++i){
  24. fu=find(e[i].u); fv=find(e[i].v);
  25. if(fu==fv) continue;
  26. ans+=1ll*(e[i].w+1)*(s[fu]*s[fv]-1);
  27. f[fu]=fv;
  28. s[fv]+=s[fu];
  29. }
  30. printf("%lld\n",ans);
  31. }
  32. return 0;
  33. }

B题:Picnic Planning (控制度数的最小生成树,DFS)

https://ac.nowcoder.com/acm/contest/1056/B

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define js ios::sync_with_Bstdio(false);cin.tie(0); cout.tie(0)
  4. typedef long long ll;
  5. inline int read() {
  6. int s = 0, w = 1; char ch = getchar();
  7. while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
  8. while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
  9. return s * w;
  10. }
  11. const int N = 37;
  12. const int INF = 0x3f3f3f3f;
  13. struct Edge {
  14. int x, y, z;
  15. bool operator < (const Edge w) const {
  16. return z < w.z;
  17. }
  18. }f[N];
  19. int n, k, tot, ans, a[N][N], fa[N], d[N], v[N];
  20. map<string, int> mp;
  21. vector<Edge> e;
  22. bool b[N][N];
  23. int find(int x) {
  24. return fa[x] == x ? x : fa[x] = find(fa[x]);
  25. }
  26. void dfs(int x, int pre) {
  27. for (int i = 2; i <= tot; ++i) {
  28. if (i == pre || !b[x][i]) continue;
  29. if (f[i].z == -1) {
  30. if (f[x].z > a[x][i]) f[i] = f[x];
  31. else {
  32. f[i].x = x;
  33. f[i].y = i;
  34. f[i].z = a[x][i];
  35. }
  36. }
  37. dfs(i, x);
  38. }
  39. }
  40. int main() {
  41. js;
  42. memset(a, 0x3f, sizeof(a));
  43. memset(d, 0x3f, sizeof(d));
  44. mp["Park"] = tot = 1;
  45. for (int i = 1; i < N; ++i) fa[i] = i;
  46. cin >> n;
  47. for (int i = 1; i <= n; ++i) {
  48. Edge w;
  49. string s1, s2;
  50. cin >> s1 >> s2 >> w.z;
  51. w.x = mp[s1] ? mp[s1] : (mp[s1] = ++tot);
  52. w.y = mp[s2] ? mp[s2] : (mp[s2] = ++tot);
  53. e.push_back(w);
  54. a[w.x][w.y] = a[w.y][w.x] = min(a[w.x][w.y], w.z);
  55. }
  56. cin >> k;
  57. sort(e.begin(), e.end());
  58. for (auto it : e) { //去掉1的连边,构成几个连通块,再把连通块并成一个求解花费
  59. if (it.x == 1 || it.y == 1) continue;
  60. int fax = find(it.x), fay = find(it.y);
  61. if (fax != fay) {
  62. fa[fax] = fay;
  63. b[it.x][it.y] = b[it.y][it.x] = 1;
  64. ans += it.z;
  65. }
  66. }
  67. for (int i = 2; i <= tot; ++i) //找连通区域内和1连接花费最小
  68. if (a[1][i] != INF) {
  69. int rt = find(i);
  70. if (d[rt] > a[1][i]) {
  71. v[rt] = i;
  72. d[rt] = a[1][v[rt]];
  73. }
  74. }
  75. for (int i = 1; i <= tot; ++i) { //先把连通区域和1连接
  76. if (d[i] != INF) {
  77. --k;
  78. b[1][v[i]] = b[v[i]][1] = 1;
  79. ans += a[1][v[i]];
  80. }
  81. }
  82. while (k--) { //调整看能不能更小一点
  83. memset(f, -1, sizeof(f));
  84. f[1].z = -INF;
  85. for (int i = 2; i <= tot; ++i)
  86. if (b[1][i]) f[i].z = -INF;
  87. dfs(1, 0);
  88. int o, w = -INF;
  89. for (int i = 2; i <= tot; ++i)
  90. if (w < f[i].z - a[1][i]) {
  91. o = i;
  92. w = f[i].z - a[1][o];
  93. }
  94. if (w <= 0) break;
  95. b[1][o] = b[o][1] = 1;
  96. b[f[o].x][f[o].y] = b[f[o].y][f[o].x] = 0;
  97. ans -= w;
  98. }
  99. cout << "Total miles driven: " << ans << endl;
  100. return 0;
  101. }

C题:最优比率生成树

https://ac.nowcoder.com/acm/contest/1056/C

0/1 规划问题,利用二分 + prim求解。

根据0/1问题模型,只需要构建一张新的无向网,图的结构不变,但每条边只有一个权值 CemidRe ,在新的无向图中求解最大生成树,若最大生成树上边权之和非负,$l = mid $,否则令 r=mid

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define dis(a,b) sqrt(pow((nod[a].x - nod[b].x), 2) + pow((nod[a].y - nod[b].y), 2))
  4. const int maxn = 5000;
  5. const int inf = 0x3f3f3f3f;
  6. struct node {
  7. double x, y, c;
  8. }nod[maxn];
  9. int n;
  10. double cost[maxn][maxn];
  11. double a[maxn][maxn];
  12. bool book[maxn];
  13. double d[maxn];
  14. double prim(double mid) {
  15. memset(book, 0, sizeof(book));
  16. for (int i = 1; i <= n; i++)
  17. d[i] = 1e8;
  18. d[1] = 0;
  19. for (int i = 1; i < n; i++) {//一共只需要进行n-1次操作
  20. int x = 0;
  21. for (int j = 1; j <= n; j++)
  22. if (!book[j] && (x == 0 || d[x] > d[j]))//找出没有用过或者距离已选遍最近的点
  23. x = j;
  24. book[x] = true;
  25. for (int y = 1; y <= n; y++)
  26. if (!book[y])d[y] = min(d[y], a[x][y] - mid * cost[x][y]);
  27. }
  28. double ans = 0.0;
  29. for (int i = 2; i <= n; i++) {
  30. ans += d[i];
  31. }
  32. return ans;
  33. }
  34. int main() {
  35. //freopen("in.txt", "r", stdin);
  36. //ios::sync_with_stdio(false), cin.tie(0);
  37. while (scanf("%d", &n), n) {
  38. for (int i = 1; i <= n; i++)
  39. scanf("%lf%lf%lf", &nod[i].x, &nod[i].y, &nod[i].c);
  40. for (int i = 1; i <= n; i++)
  41. for (int j = 1; j <= n; j++) {
  42. cost[i][j] = dis(i, j);
  43. a[i][j] = fabs(nod[i].c - nod[j].c);
  44. }
  45. double r = inf, l = 0;
  46. while (r - l > 0.000001) {
  47. double mid = (l + r) / 2;
  48. double ans = prim(mid);
  49. if (ans == 0)
  50. break;
  51. else if (ans > 0)
  52. l = mid;
  53. else
  54. r = mid;
  55. }
  56. printf("%.3lf\n", (r + l) / 2);
  57. }
  58. }

D题:黑暗城堡 (最短路径生成树)

先跑一次dijkstra
对于构造一个树的过程,每个节点都会选择一个节点插入树中
只需要统计每个点能够选择哪些点(满足到1号点距离最小)去插入即可。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn=1e3+10;
  4. int e[maxn][maxn],inf=1e9;
  5. int dis[maxn],vis[maxn];
  6. const int mod=(1LL<<31)-1;
  7. struct node
  8. {
  9. int s,id;
  10. bool operator < (const node& b) const{
  11. return (this->s)<b.s;
  12. }
  13. }q[maxn];
  14. int main()
  15. {
  16. int n,m;scanf("%d%d",&n,&m);
  17. for(int i=1;i<=n;i++){
  18. for(int j=1;j<i;j++)
  19. e[i][j]=e[j][i]=inf;
  20. }
  21. for(int i=1;i<=m;i++){
  22. int t1,t2,t3;
  23. scanf("%d%d%d",&t1,&t2,&t3);e[t1][t2]=e[t2][t1]=t3;
  24. }
  25. for(int i=1;i<=n;i++) dis[i]=inf;
  26. dis[1]=0;
  27. for(int i=1;i<n;i++){
  28. int u,mx=1e9;
  29. for(int j=1;j<=n;j++){
  30. if(vis[j]) continue;
  31. if(dis[j]<mx){
  32. u=j;mx=dis[j];
  33. }
  34. }
  35. vis[u]=1;
  36. for(int j=1;j<=n;j++){
  37. if(dis[j]>dis[u]+e[u][j]){
  38. dis[j]=dis[u]+e[u][j];
  39. }
  40. }
  41. }
  42. for(int i=1;i<=n;i++){
  43. q[i].s=dis[i];q[i].id=i;
  44. }
  45. sort(q+1,q+1+n);int ans=1;
  46. for(int i=2;i<=n;i++){
  47. int cnt=0;
  48. for(int j=1;j<=n;j++){
  49. if(j==i) continue;
  50. if(dis[i]==dis[j]+e[j][i]) cnt++;
  51. }
  52. //cout<<i<<" "<<cnt<<endl;
  53. ans=1LL*ans*cnt%mod;
  54. }
  55. cout<<ans<<endl;
  56. }
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号