[关闭]
@DATASOURCE 2015-09-04T17:39:04.000000Z 字数 3438 阅读 1778

最小限度生成树

图论 生成树


简述

最小度限制生成树就是给一个图,求它的满足点vo的度数最大为k的最小生成树.

算法

  1. 将v0从图中删除,将得到m个连通分量。
  2. 对每个连通分量求最小生成树,假设m个。
  3. 从每个连通分量中找与v0关联的权值最小的边,与v0相连接,这样将得到v0的最小m度生成树
  4. 如果 k < m 那么这种树是不存在的。
  5. 如果 k >= m, 那么考虑构建 m + 1度最小生成树, 将与v0关联的且不在当前的树中的边
  6. 如果将其加入树中, 必然会存在一个环,那么删掉该环中与v0不关联的权值最大边,将得到加入该边后的最小生成树,且是m + 1的。
  7. 枚举上述 6 的边找树权值最小, 那么即是m + 1度限制的最小生成树。如果 m + 1 度最小生成树的值大于 m 度最小生成树的话直接输出当前 m 度的值即可。
  8. 重复5.6.7,直到k 度最小生成树出现。

由上述步骤可知,由 m 度限制生成树拓展为 m + 1 度限制生成树的过程中需要大量的重复运算, 可以运用动态规划来优化。

设 dp(v) 为路径 v0—v 上与 v0 无关联且权值最大的边。定义 father(v) 为 v 的父结点,动态转移方程

dp(v)=max(dp(father(v)),ω(father(v),v))
边界条件为
dp[v0]=(,)dp[v]=|(v0,v)E(T)

算法实现

并查集 + kruskal;
首先,每个连通分量的的最小生成树可以直接用一个循环,循环着 Kruskal 求出;
这里利用了联通分量间的独立性,对每个连通分量分别求最小生成树,和放在一起求,毫不影响;
而且 kruskral 算法保证了各连通分量边的有序性;
找最小边的时候,可以用动态规划,也可以这么做:
先走一个循环,但我们需要逆过来加边,将与 v0 关联的所有边从小到达排序;
然后将各连通分量连接起来,利用并查集可以保证每个连通分量只有一条边与 v0 相连;
由于边已经从小到达排序,故与每个连通分量相连的边就是每个连通分量与 v0 相连中的最小边;
然后求 m + 1度的最小生成树时,可以直接用DFS,最小生成树要一直求到k度,然后从中找出一个最优值;

例题 POJ 1639

  • 题意:
    给出m条边,每条边有两个端点和一个权值;
    求这个图在满足以下条件的情况下的最小生成树:
    在所有点中,有一个特殊点Park,它在求得的最小生成树中的度必须小于等于某个值;

  • 示例代码

  1. #include <map>
  2. #include <cstdio>
  3. #include <string>
  4. #include <iostream>
  5. #include <algorithm>
  6. using namespace std;
  7. const int MAXN = 50;
  8. const int MAX_INT = (1 << 29);
  9. struct Edge{
  10. int v, w, nxt;
  11. };
  12. struct Node{
  13. int u, v, w;
  14. friend bool operator < (const Node &a, const Node &b){
  15. return a.w < b.w;
  16. }
  17. };
  18. int pcnt;
  19. int par[MAXN];
  20. map <string, int> ma;
  21. Node node[MAXN * MAXN], dp[MAXN];
  22. int ecnt, n, m, k;
  23. bool use[MAXN][MAXN];
  24. Edge edge[MAXN * MAXN];
  25. int head[MAXN], vis[MAXN], dis[MAXN];
  26. int Min[MAXN], tmp[MAXN];
  27. void init(){
  28. ma.clear();
  29. ecnt = pcnt = 0;
  30. memset(dp, -1, sizeof(dp));
  31. memset(use, 0, sizeof(use));
  32. memset(edge, 0, sizeof(edge));
  33. memset(node, 0, sizeof(node));
  34. memset(head, -1, sizeof(head));
  35. fill(Min, Min + MAXN, MAX_INT);
  36. for(int i = 0; i < MAXN; i++) par[i] = i;
  37. }
  38. void addEdge(int u, int v, int w){
  39. edge[ecnt].v = v;
  40. edge[ecnt].w = w;
  41. edge[ecnt].nxt = head[u];
  42. head[u] = ecnt++;
  43. }
  44. int getId(char s[]){
  45. if(ma.find(s) == ma.end()) ma[s] = ++pcnt;
  46. else return ma[s];
  47. return pcnt;
  48. }
  49. int Find(int x){
  50. if(par[x] != x) par[x] = Find(par[x]);
  51. return par[x];
  52. }
  53. bool Union(int x, int y){
  54. int fx = Find(x);
  55. int fy = Find(y);
  56. if(fx == fy) return false;
  57. par[fy] = fx;
  58. return true;
  59. }
  60. void dfs(int s, int u, int fa){
  61. for(int i = head[u]; i + 1; i = edge[i].nxt){
  62. int v = edge[i].v;
  63. if(v != s && v != fa && use[u][v]){
  64. if(dp[v].w == -1){
  65. if(dp[u].w > edge[i].w) dp[v] = dp[u];
  66. else{
  67. dp[v].w = edge[i].w;
  68. dp[v].u = u;
  69. dp[v].v = v;
  70. }
  71. }
  72. dfs(s, v, u);
  73. }
  74. }
  75. }
  76. int kruskal(int s){
  77. int ans = 0;
  78. for(int i = 0; i < n; i++){
  79. if(node[i].u == s || node[i].v == s) continue;
  80. if(!Union(node[i].u, node[i].v)) continue;
  81. use[node[i].u][node[i].v] = use[node[i].v][node[i].u] = true;
  82. ans += node[i].w;
  83. }
  84. return ans;
  85. }
  86. int solve(int s){
  87. sort(node, node + n);
  88. int ans = kruskal(s);
  89. for(int i = head[s]; i + 1; i = edge[i].nxt){
  90. int v = edge[i].v;
  91. int belong = Find(v);
  92. if(Min[belong] > edge[i].w){
  93. tmp[belong] = edge[i].v;
  94. Min[belong] = edge[i].w;
  95. }
  96. }
  97. int degree = 0;
  98. for(int i = 1; i <= pcnt; i++){
  99. if(Min[i] != MAX_INT){
  100. degree++;
  101. use[s][tmp[i]] = use[tmp[i]][s] = true;
  102. ans += Min[i];
  103. }
  104. }
  105. for(int i = degree + 1; i <= k; i++){
  106. dp[s].w = -MAX_INT;
  107. for(int j = 2; j <= pcnt; j++)
  108. if(use[s][j]) dp[j].w = -MAX_INT;
  109. dfs(s, 1, -1);
  110. int temp, mi = MAX_INT;
  111. for(int j = head[s]; j + 1; j = edge[j].nxt){
  112. if(mi > edge[j].w - dp[edge[j].v].w){
  113. mi = edge[j].w - dp[edge[j].v].w;
  114. temp = edge[j].v;
  115. }
  116. }
  117. if(mi >= 0) break;
  118. use[s][temp] = use[temp][s] = true;
  119. int u = dp[temp].u;
  120. int v = dp[temp].v;
  121. use[u][v] = use[v][u] = false;
  122. ans += mi;
  123. }
  124. return ans;
  125. }
  126. int main(){
  127. while(scanf("%d", &n) != EOF){
  128. init();
  129. int u, v, w;
  130. char s1[50];
  131. char s2[50];
  132. ma["Park"] = ++pcnt;
  133. for(int i = 0; i < n; i++){
  134. scanf("%s%s%d", s1, s2, &w);
  135. u = getId(s1), v = getId(s2);
  136. addEdge(u, v, w);
  137. addEdge(v, u, w);
  138. node[i].u = u, node[i].v = v, node[i].w = w;
  139. }
  140. scanf("%d", &k);
  141. int ans = solve(1);
  142. printf("Total miles driven: %d\n", ans);
  143. }
  144. return 0;
  145. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注