【题目描述】

给你一个图,n个点,m条边,求一颗生成树满足如下条件:

(1)结点1的度不超过k。

(2)在(1)条件下所求生成树最小。

【算法引入】

最小k度限制生成树,就是指有特殊的某一点的度不能超过k时的最小生成树。

如果T是G的一个生成树且dT(v0)=k,则称T为G的k度限制生成树。

G中权值和最小的k度限制生成树称为G的最小k度生成树。

【算法思想】

设特殊的那点为v0,先把v0删除,求出剩下连通图的所有最小生成树。

假如有m棵最小生成树,那么这些生成树必定要跟v0点相连。

也就是说这棵生成树的v0点至少是m度的。

若m>k,条件不成立,无法找到最小k度限制生成树。

若m<=k,则枚举m到k的所有最小生成树,即一步步将v0点的度加1,直到v0点的度为k为止。

则v0点度从m到k的(k-m+1)棵最小生成树中最小的那棵即为答案。

【算法步骤】

(1) 原图中去掉和V0相连的所有边(可以先存两个图,建议一个邻接矩阵,一个边表,用方便枚举边的邻接表来构造新图)。

得到m个连通分量,则这m个连通分量必须通过v0来连接。

则在图G的所有生成树中dT(v0)>=m。

则当k<m时,问题无解。

对每个连通分量求一次最小生成树。

每个连通分量的的最小生成树可以直接用一个循环,循环着Kruskal求出。

这里利用了联通分量间的独立性,对每个连通分量分别求最小生成树,和放在一起求,毫不影响。

而且kruskral算法保证了各连通分量边的有序性。

  1. int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}//路径压缩
  2. bool cmp(node a,node b) {return a.v<b.v;}
  3. void Kruskal()
  4. {
  5. for(int i=;i<=n;i++) f[i]=i;//并查集初始化
  6. sort(e+,e+m+,cmp);//按边权排序
  7. for(int i=;i<=m;i++)
  8. {
  9. if(e[i].x==||e[i].y==) continue;
  10. int x=find(e[i].x),y=find(e[i].y);
  11. if(x!=y)
  12. {
  13. f[x]=y;
  14. check[e[i].x][e[i].y]=check[e[i].y][e[i].x]=;//check表示有边相连
  15. ans+=e[i].v;
  16. }
  17. }
  18. }

(2) 对于每个连通分量V’,用一条与V0直接连接的最小的边把它与V0点连接起来,使其整体成为一个生成树。

就得到了一个m度限制生成树,即为最小m度限制生成树。

  1. void solve1()//计算最小m度生成树
  2. {
  3. for(int i=;i<=n;i++) Min[i]=INF;
  4. for(int i=;i<=n;i++)//找出从1点连到每个连通块的最小边权
  5. if(a[][i]!=-)
  6. {
  7. int t=find(i);
  8. if(a[i][]<Min[t])
  9. {
  10. Min[t]=a[i][];
  11. temp[t]=i;
  12. }
  13. }
  14. for(int i=;i<=n;i++)//把1点和每个连通块连接起来,计算答案
  15. if(Min[i]!=INF)
  16. {
  17. md++;
  18. check[][temp[i]]=check[temp[i]][]=;
  19. ans+=a[][temp[i]];
  20. }
  21. }

(3)由最小m度限制生成树得到最小m+1度限制生成树。

连接和V0相邻的点v,则可以知道一定会有一个环出现(因为原来是一个生成树);

只要找到这个环上的最大权边(不能与v0点直接相连)并删除,就可以得到一个m+1度限制生成树;

枚举所有和V0相邻点v,找到替换后,增加权值最小的一次替换(如果找不到这样的边,就说明已经求出);

就可以求得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);

当dT(v0)=k时停止(即当V0的度为k的时候停止),但不一定k的时候最优;

  1. void dfs(int x,int fa)//动规计算dp,dp记录的是从1到某点路径中权值最大的边,且此边不与1相连
  2. {
  3. for(int i=;i<=n;i++)//枚举点
  4. if(check[x][i]&&i!=fa)//有边相连
  5. {
  6. if(dp[i].v==-)//没被搜过,更新dp
  7. {
  8. if(a[x][i]<dp[x].v) dp[i]=dp[x];
  9. else dp[i].x=x,dp[i].y=i,dp[i].v=a[x][i];
  10. }
  11. dfs(i,x);
  12. }
  13. }
  14. void solve2()//计算最小k度生成树
  15. {
  16. for(int i=md+;i<=k;i++)
  17. {
  18. memset(dp,-,sizeof(dp)); dp[].v=-INF;
  19. for(int j=;j<=n;j++) if(check[][j]) e[j].v=-INF;//把与1相连的边设为-oo,避免dfs时搜到
  20. dfs(,); int t=,minn=INF;
  21. for(int j=;j<=n;j++)
  22. if(a[][j]!=-&&a[][j]-dp[j].v<minn)//记录对答案贡献最大的边
  23. {
  24. minn=a[][j]-dp[j].v;
  25. t=j;
  26. }
  27. if(minn>=) break;//找不到就说明已经增广完了
  28. int x=dp[t].x,y=dp[t].y;
  29. check[][t]=check[t][]=;//维护图的性质
  30. check[x][y]=check[y][x]=;
  31. ans+=minn;
  32. }
  33. }

【算法实现】

完整代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<cmath>
  6. #include<ctime>
  7. #include<algorithm>
  8. using namespace std;
  9. #define INF 1000000000
  10. #define MAXN 105
  11. struct node{int x,y,v;}e[MAXN],dp[MAXN];//e是图的边表
  12. int n,m,k,ans,md,f[MAXN],Min[MAXN],temp[MAXN],a[MAXN][MAXN],check[MAXN][MAXN];
  13. inline int read()
  14. {
  15. int x=,f=; char ch=getchar();
  16. while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
  17. while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
  18. return x*f;
  19. }
  20. void init()
  21. {
  22. n=read(); m=read(); k=read();//n是结点数,m是边数,k是度数限制
  23. memset(a,-,sizeof(a));//a是图的邻接矩阵
  24. for(int i=;i<=m;i++)
  25. {
  26. int x=read(),y=read(),v=read();
  27. e[i].x=x; e[i].y=y; e[i].v=v;
  28. if(a[x][y]==-) a[x][y]=a[y][x]=v;
  29. else a[x][y]=a[y][x]=min(a[x][y],v); //判重边
  30. }
  31. }
  32. int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}//路径压缩
  33. bool cmp(node a,node b) {return a.v<b.v;}
  34. void Kruskal()
  35. {
  36. for(int i=;i<=n;i++) f[i]=i;//并查集初始化
  37. sort(e+,e+m+,cmp);//按边权排序
  38. for(int i=;i<=m;i++)
  39. {
  40. if(e[i].x==||e[i].y==) continue;
  41. int x=find(e[i].x),y=find(e[i].y);
  42. if(x!=y)
  43. {
  44. f[x]=y;
  45. check[e[i].x][e[i].y]=check[e[i].y][e[i].x]=;//check表示有边相连
  46. ans+=e[i].v;
  47. }
  48. }
  49. }
  50. void solve1()//计算最小m度生成树
  51. {
  52. for(int i=;i<=n;i++) Min[i]=INF;
  53. for(int i=;i<=n;i++)//找出从1点连到每个连通块的最小边权
  54. if(a[][i]!=-)
  55. {
  56. int t=find(i);
  57. if(a[i][]<Min[t])
  58. {
  59. Min[t]=a[i][];
  60. temp[t]=i;
  61. }
  62. }
  63. for(int i=;i<=n;i++)//把1点和每个连通块连接起来,计算答案
  64. if(Min[i]!=INF)
  65. {
  66. md++;
  67. check[][temp[i]]=check[temp[i]][]=;
  68. ans+=a[][temp[i]];
  69. }
  70. }
  71. void dfs(int x,int fa)//动规计算dp,dp记录的是从1到某点路径中权值最大的边,且此边不与1相连
  72. {
  73. for(int i=;i<=n;i++)//枚举点
  74. if(check[x][i]&&i!=fa)//有边相连
  75. {
  76. if(dp[i].v==-)//没被搜过,更新dp
  77. {
  78. if(a[x][i]<dp[x].v) dp[i]=dp[x];
  79. else dp[i].x=x,dp[i].y=i,dp[i].v=a[x][i];
  80. }
  81. dfs(i,x);
  82. }
  83. }
  84. void solve2()//计算最小k度生成树
  85. {
  86. for(int i=md+;i<=k;i++)
  87. {
  88. memset(dp,-,sizeof(dp)); dp[].v=-INF;
  89. for(int j=;j<=n;j++) if(check[][j]) e[j].v=-INF;//把与1相连的边设为-oo,避免dfs时搜到
  90. dfs(,); int t=,minn=INF;
  91. for(int j=;j<=n;j++)
  92. if(a[][j]!=-&&a[][j]-dp[j].v<minn)//记录对答案贡献最大的边
  93. {
  94. minn=a[][j]-dp[j].v;
  95. t=j;
  96. }
  97. if(minn>=) break;//找不到就说明已经增广完了
  98. int x=dp[t].x,y=dp[t].y;
  99. check[][t]=check[t][]=;//维护图的性质
  100. check[x][y]=check[y][x]=;
  101. ans+=minn;
  102. }
  103. }
  104. int main()
  105. {
  106. freopen("cin.in","r",stdin);
  107. freopen("cout.out","w",stdout);
  108. init();
  109. Kruskal();
  110. solve1();
  111. solve2();
  112. printf("%d\n",ans);
  113. return ;
  114. }

【例题一】poj 1639

题目大意:

给出m条边,每条边有两个端点和一个权值,求这个图在满足以下条件的情况下的最小生成树:

在所有点中,有一个特殊点Park,它在求得的最小生成树中的度必须小于等于某个值。

这题需要注意在输入时处理字符串,把Park设为根结点,然后用上述算法即可。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<cmath>
  6. #include<ctime>
  7. #include<algorithm>
  8. #include<string>
  9. #include<map>
  10. using namespace std;
  11. #define INF 1000000000
  12. #define MAXN 105
  13. struct node{int x,y,v;}e[MAXN*MAXN],dp[MAXN];
  14. int n(),m,K,oo,ans,md,Min[MAXN],temp[MAXN],f[MAXN],a[MAXN][MAXN],check[MAXN][MAXN];
  15. string s;
  16. map<string,int> Map;
  17. inline int read()
  18. {
  19. int x=,f=; char ch=getchar();
  20. while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
  21. while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
  22. return x*f;
  23. }
  24. bool cmp(node a,node b) {return a.v<b.v;}
  25. int cal() {if(Map.find(s)==Map.end()) Map[s]=++n; return Map[s];}
  26. int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
  27. void init()
  28. {
  29. m=read(); Map["Park"]=;
  30. memset(a,-,sizeof(a));
  31. memset(Min,,sizeof(Min));
  32. oo=Min[];
  33. for(int i=;i<=m;i++)
  34. {
  35. cin>>s; e[i].x=cal();
  36. cin>>s; e[i].y=cal();
  37. e[i].v=read();
  38. if(a[e[i].x][e[i].y]==-) a[e[i].y][e[i].x]=a[e[i].x][e[i].y]=e[i].v;
  39. else a[e[i].x][e[i].y]=a[e[i].y][e[i].x]=min(a[e[i].x][e[i].y],e[i].v);
  40. }
  41. K=read();
  42. }
  43. void kruskal()
  44. {
  45. for(int i=;i<=n;i++) f[i]=i;
  46. sort(e+,e+m+,cmp);
  47. for(int i=;i<=m;i++)
  48. {
  49. if(e[i].x==||e[i].y==) continue;
  50. int x=find(e[i].x),y=find(e[i].y);
  51. if(x==y) continue;
  52. check[e[i].x][e[i].y]=check[e[i].y][e[i].x]=;
  53. f[y]=x;
  54. ans+=e[i].v;
  55. }
  56. }
  57. void solve1()
  58. {
  59. for(int i=;i<=n;i++)
  60. if(a[i][]!=-)
  61. {
  62. int t=find(i);
  63. if(a[i][]<Min[t])
  64. {
  65. temp[t]=i;
  66. Min[t]=a[i][];
  67. }
  68. }
  69. for(int i=;i<=n;i++)
  70. if(Min[i]!=oo)
  71. {
  72. md++;
  73. check[][temp[i]]=check[temp[i]][]=;
  74. ans+=a[][temp[i]];
  75. }
  76. }
  77. void dfs(int x,int fa)
  78. {
  79. for(int i=;i<=n;i++)
  80. if(check[x][i]&&i!=fa)
  81. {
  82. if(dp[i].v==-)
  83. {
  84. if(a[x][i]<dp[x].v) dp[i]=dp[x];
  85. else dp[i].x=x,dp[i].y=i,dp[i].v=a[x][i];
  86. }
  87. dfs(i,x);
  88. }
  89. }
  90. void solve2()
  91. {
  92. for(int i=md+;i<=K;i++)
  93. {
  94. memset(dp,-,sizeof(dp)); dp[].v=-INF;
  95. for(int j=;j<=n;j++) if(check[][j]) dp[j].v=-INF;
  96. dfs(,-); int t=,minn=INF;
  97. for(int j=;j<=n;j++)
  98. if(a[][j]!=-&&a[][j]-dp[j].v<minn)
  99. {
  100. minn=a[][j]-dp[j].v;
  101. t=j;
  102. }
  103. if(minn>=) break;
  104. check[][t]=check[t][]=;
  105. int x=dp[t].x,y=dp[t].y;
  106. check[x][y]=check[y][x]=;
  107. ans+=minn;
  108. }
  109. }
  110. int main()
  111. {
  112. init();
  113. kruskal();
  114. solve1();
  115. solve2();
  116. printf("Total miles driven: %d\n",ans);
  117. return ;
  118. }

【例题二】poj 2349

题目大意:

某地区共有n座村庄,每座村庄的坐标用一对整数(x, y)表示,现在要在村庄之间建立通讯网络。通讯工具有两种,分别是需要铺设的普通线路和卫星设备。卫星设备数量有限,只能给k个村庄配备卫星设备。拥有卫星设

备的村庄互相间直接通讯;铺设了线路的村庄之间也可以通讯。卫星分配是不受限制的。

输入:

第一行:数据组数CASE

接下来每组数据第一行:k,n,分别为卫星数和村庄数。

接下来n行,每行2个数,x,y,表示村庄的坐标。

输出:

最短通讯网络中最长的路。

题解详见:2004年国家集训队论文——汪汀《最小生成树问题的扩展》

附参考代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<cmath>
  6. #include<ctime>
  7. #include<algorithm>
  8. using namespace std;
  9. #define INF 1000000000
  10. #define MAXN 505
  11. struct node{int x,y;double v;}e[MAXN*MAXN],dp[MAXN*MAXN];
  12. int n,m,k,md,X[MAXN],Y[MAXN],f[MAXN],temp[MAXN],check[MAXN][MAXN];
  13. double ans,Min[MAXN],a[MAXN][MAXN];
  14. inline int read()
  15. {
  16. int x=,f=; char ch=getchar();
  17. while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
  18. while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
  19. return x*f;
  20. }
  21. double cal(int a,int b) {return sqrt(((X[a]-X[b])*(X[a]-X[b])+(Y[a]-Y[b])*(Y[a]-Y[b]))*1.00);}
  22. void insert(int x,int y,double v)
  23. {
  24. e[++m].x=x;e[m].y=y;e[m].v=v;
  25. if(a[x][y]==-) a[x][y]=a[y][x]=v;
  26. else a[x][y]=a[y][x]=(a[x][y]<v?v:a[x][y]);
  27. }
  28. void init()
  29. {
  30. memset(a,,sizeof(a));
  31. k=read(); n=read();
  32. for(int i=;i<=n;i++)
  33. {
  34. X[i]=read(),Y[i]=read();
  35. for(int j=;j<i;j++) insert(i+,j+,cal(i,j));
  36. }
  37. for(int i=;i<=n;i++) insert(,i+,);
  38. n++;
  39. }
  40. bool cmp(node a,node b) {return a.v<b.v;}
  41. int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
  42. void Kruskal()
  43. {
  44. for(int i=;i<=n;i++) f[i]=i;
  45. sort(e+,e+m+,cmp);
  46. for(int i=;i<=m;i++)
  47. {
  48. if(e[i].x==||e[i].y==) continue;
  49. int x=find(e[i].x),y=find(e[i].y);
  50. if(x==y) continue;
  51. f[x]=y;
  52. check[e[i].x][e[i].y]=check[e[i].y][e[i].x]=;
  53. }
  54. }
  55. void solve1()
  56. {
  57. for(int i=;i<=n;i++) Min[i]=INF;
  58. for(int i=;i<=n;i++)
  59. if(a[][i]!=-)
  60. {
  61. int t=find(i);
  62. if(a[i][]<Min[t])
  63. {
  64. Min[t]=a[i][];
  65. temp[t]=i;
  66. }
  67. }
  68. for(int i=;i<=n;i++)
  69. if(Min[i]!=INF)
  70. {
  71. md++;
  72. check[][temp[i]]=check[temp[i]][]=;
  73. }
  74. }
  75. void dfs(int x,int fa)
  76. {
  77. for(int i=;i<=n;i++)
  78. if(check[x][i]&&i!=fa)
  79. {
  80. if(dp[i].v==-)
  81. {
  82. if(a[x][i]<dp[x].v) dp[i]=dp[x];
  83. else dp[i].x=x,dp[i].y=i,dp[i].v=a[x][i];
  84. }
  85. dfs(i,x);
  86. }
  87. }
  88. void solve2()
  89. {
  90. for(int i=md+;i<=k;i++)
  91. {
  92. for(int i=;i<=n;i++) dp[i].v=-; dp[].v=-INF;
  93. for(int j=;j<=n;j++) if(check[][j]) e[j].v=-INF;
  94. dfs(,); int t=; double minn=INF;
  95. for(int j=;j<=n;j++)
  96. if(a[][j]!=-&&a[][j]-dp[j].v<minn)
  97. {
  98. minn=a[][j]-dp[j].v;
  99. t=j;
  100. }
  101. if(minn>=) break;
  102. int x=dp[t].x,y=dp[t].y;
  103. check[][t]=check[t][]=;
  104. check[x][y]=check[y][x]=;
  105. }
  106. }
  107. void pre()
  108. {
  109. n=m=k=md=; ans=;
  110. memset(check,,sizeof(check));
  111. }
  112. int main()
  113. {
  114. //freopen("cin.in","r",stdin);
  115. //freopen("cout.out","w",stdout);
  116. int CASE=read();
  117. while(CASE--)
  118. {
  119. pre();
  120. init();
  121. Kruskal();
  122. solve1();
  123. solve2();
  124. for(int i=;i<=n;i++)
  125. for(int j=;j<=n;j++)
  126. if(check[i][j]&&a[i][j]>ans) ans=a[i][j];
  127. printf("%.2lf\n",ans);
  128. }
  129. return ;
  130. }

最小k度限制生成树的更多相关文章

  1. 【POJ 1639】 Picnic Planning (最小k度限制生成树)

    [题意] 有n个巨人要去Park聚会.巨人A和先到巨人B那里去,然后和巨人B一起去Park.B君是个土豪,他家的停车场很大,可以停很多车,但是Park的停车场是比较小.只能停k辆车.现在问你在这个限制 ...

  2. poj 1639 最小k度限制生成树

    题目链接:https://vjudge.net/problem 题意: 给各位看一下题意,算法详解看下面大佬博客吧,写的很好. 参考博客:最小k度限制生成树 - chty - 博客园  https:/ ...

  3. poj1639 Picnic Planning,K度限制生成树

    题意: 矮人虽小却喜欢乘坐巨大的轿车,车大到能够装下不管多少矮人.某天,N(N≤20)个矮人打算到野外聚餐.为了集中到聚餐地点,矮人A 要么开车到矮人B 家中,留下自己的轿车在矮人B 家,然后乘坐B ...

  4. 最小k度最小生成树模板

    代码是抄的 题解是瞄的 可我想学习的心是真的嘤嘤嘤 然而 还是上传一份ioi大神的论文吧 链接:https://pan.baidu.com/s/1neIW9QeZEa0hXsUqJTjmeQ 密码:b ...

  5. Picnic Planning POJ - 1639(最小k度生成树)

    The Contortion Brothers are a famous set of circus clowns, known worldwide for their incredible abil ...

  6. K度限制MST poj 1639

    /* k度限制MST:有一个点的度<=k的MST poj 1639 要求1号点的度不超过k 求MST 我们先把1号点扔掉 跑MST 假设有sum个连通分支 然后把这sum个分支连到1上 就得到了 ...

  7. HDU 4862 Jump(最小K路径覆盖)

    输入一个n×m网格图,每个结点的值为0-9,可以从任意点出发不超过k次,走完每个点且仅访问每个结点一次,问最终的能量最大值.不可全部走完的情况输出-1. 初始能量为0. 而结点(x,y)可以跳跃到结点 ...

  8. 求n个数中的最大或最小k个数

    //求n个数中的最小k个数        public static void TestMin(int k, int n)        {            Random rd = new Ra ...

  9. nyoj 678 最小K个数之和

    最小K个数之和 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 输入n个整数,输出其中最小的K个数之和.例如输入4,5,1,1,6,2,7,3,3这9个数字,当k=4 ...

随机推荐

  1. jqGrid 获取多级标题表头

    1.jgGrid没有提供此方法获取如下标题 2.实现代码 getHeaders:function(){ var headers=[],temptrs=[]; //select the group he ...

  2. leetcode1016

    class Solution(object): def queryString(self, S: str, N: int) -> bool: return all(S.find(bin(i)[2 ...

  3. zipfile模块

    在python中操作zip文件, 基本上都是使用zipfile模块,他可以创建.解压文件,获取zip文件的元数据信息. 我们想要操作一个zip文件,第一步就是初始化ZipFile实例. 1.打开tes ...

  4. js实现上传前删除指定图片

    "上传之前"移除选错图片代码: 此处效果为:点击需要删除的图片,确认删除就可以了.

  5. block原理

    block原理 block的本质是一个结构体,包含引用的外部变量及一个需要执行的函数的函数指针,在内存中可以有三个位置,即堆上.栈上和全局区(静态区).当block中没有引用外部变量时,block的位 ...

  6. jquery接触初级-----juqery DOM操作 之二

    DOm 操作之: 1.1  children(),这个函数只是查找元素的子元素,而不考虑其他后代元素 <body> <p title="请选择你最喜欢的水果"&g ...

  7. Linux性能测试分析命令_iostat

    iostat用于输出CPU和磁盘I/O相关的统计信息 iostat语法 用法:iostat [ 选项 ] [ <时间间隔> [ <次数> ]] 常用选项说明: -c:只显示系统 ...

  8. 遍历DOM树,理解更新范围

    在JavaScript中,如果需求对多个元素重复进行同样的操作,就需要写一个循环来遍历选中的所有元素. 在jQuery中,当选择器返回了多个元素时,可以使用一个方法来更新所有的元素,不再需要使用循环. ...

  9. C# winform进度条 (异步)

    进度条页面: http://www.cnblogs.com/Deckard/archive/2009/06/24/1510451.html //============================ ...

  10. [PHP]更新中间关联表数据的两种思路

    ---------------------------------------------------------------------------------------------------- ...