最小度限制生成树

最小度限制生成树

具体讲解和证明,黑书上有,IOI2004国家集训队论文--王汀 中也有讲解, 这里简单介绍求法过程。

为了方便叙述,把顶点V0的度数<=K称作度限制条件,把满足这一条件的生成树称为度限制生成树,把权值和最小的度限制生成树称为最小度限制生成树

要求的最小K度生成树,应该有以下的步骤:

算法框架:

1. 先求出最小m度限制生成树;
2. 由最小m度限制生成树得到最小m+1度限制生成树;
3. 当dT(v0)=k时停止(即当V0的度为k的时候停止);

部分引用论文:

第一步求解最小m度限制生成树:原图中去掉和V0相连的所有边,得到m个连通分量,而这m 个连通分量必须通过v0来连接,所以,在图G 的所有生成树中dT(v0)≥m。也就是说,当k<m时,问题无解。对每个连通分量求一次最小生成树,对于每个连通分量V’,求一点v1,v1∈V',且ω(v0,v1)=min{ω(v0,v')|v'∈V'},则该连通分量通过边(v1,v0)与v0相连。于是,我们就得到了一个m度限制生成树,不难证明,这就是最小m度限制生成树。

第二步,由最小m度限制生成树,得到最小m+1度限制生成树,对于和V0相邻的点v,则可以知道一定会有一个环出现,只要找到这个环上的最大权边,用边(V0, v)替换掉,就可以得到一个m+1度限制生成树,枚举所有和V0相邻点v,找到替换后增加权值最小的一次替换,就可以求得m+1度限制生成树。。如果每添加一条边,都需要对环上的边一一枚
举,时间复杂度将比较高,这里,动态规划就有了用武之地。设Best(v)为路径v0—v上与v0无关联且权值最大的边。定义father(v)为v的父结点,动态转移方程:Best(v)=max(Best(father(v)),ω(father(v),v)),边界条件为Best[v0]=-∞,Best[v’]=-∞| (v0,v’)∈E(T)。

第三步, 当度为K的时候就可以退出了。

 

pku1639  Picnic Planning 野餐计划

这里就是一个度限制生成树的应用,聚集地点最多能放K辆轿车,把聚集地点park 作为度限制点,就可以求所有小于等于K的度限制生成树,取他们中最小值,即为这题的解。

附上一份代码,写的比较乱,仅供参考。

 

复制代码
     
     
     
     
1 #include < stdio.h > 2 #include < string .h > 3   #define NN 30 4   #define INF 0x3fffffff 5   int idx, S; // S为需要限制度的那一点 6   int k, mst; // k表示k度限制,mst为最后的结果 7   int pre[NN]; 8   int mark[NN]; 9   int dis[NN]; 10   int vis[NN]; 11 char str[NN][NN]; 12 int map[NN][NN]; 13 int best[NN]; // 存的是最大权值边的终点 14 int edg[NN][NN]; // edg[i][j] = 1 表示边[i,j]已在生成树中 15 int father[NN]; // 生成树中的父节点 16 17 int find( char s[]){ 18 int i; 19 for (i = 0 ; i < idx; i ++ ){ 20 if (strcmp(str[i], s) == 0 ) return i; 21 } 22 return - 1 ; 23 } 24 25 void dfs( int cur){ // 将树拉成有根树 26 int i; 27 for (i = 0 ; i < idx; i ++ ){ 28 if (edg[i][cur] && mark[i]){ 29 father[i] = cur; 30 mark[i] = 0 ; 31 dfs(i); 32 } 33 } 34 } 35 36 int prim( int s){ // 求最小生成树 37 int i, key, Min; 38 int sum = 0 ; 39 memset(pre, 0 , sizeof (pre)); 40 for (i = 0 ; i < idx; i ++ ){ 41 dis[i] = map[s][i]; 42 pre[i] = s; 43 } 44 memset(mark, 0 , sizeof (mark)); 45 mark[s] = 1 ; 46 vis[s] = 1 ; 47 48 while ( 1 ){ 49 Min = INF; 50 key = - 1 ; 51 for (i = 0 ; i < idx; i ++ ){ 52 if ( ! vis[i] && ! mark[i] && dis[i] < Min){ 53 key = i; 54 Min = dis[i]; 55 } 56 } 57 if (key == - 1 ) break ; 58 mark[key] = 1 ; 59 vis[key] = 1 ; 60 edg[pre[key]][key] = edg[key][pre[key]] = 1 ; 61 sum += dis[key]; 62 for (i = 0 ; i < idx; i ++ ){ 63 if ( ! vis[i] && ! mark[i] && dis[i] > map[key][i]){ 64 dis[i] = map[key][i]; 65 pre[i] = key; 66 } 67 } 68 } 69 Min = INF; 70 int root = - 1 ; // 树根 71 for (i = 0 ; i < idx; i ++ ){ 72 if (mark[i] && map[i][S] < Min){ 73 Min = map[i][S]; 74 root = i; 75 } 76 } 77 // 拉成有根树 78 mark[root] = 0 ; 79 dfs(root); 80 father[root] = S; 81 return sum + Min; 82 } 83 84 int Best( int x){ // 求得x到S路径上的最大权值边 85 int tmp; 86 if (father[x] == S) return - 1 ; 87 if (best[x] != - 1 ){ 88 return best[x]; 89 } 90 tmp = Best(father[x]); 91 if (tmp != - 1 && map[tmp][father[tmp]] > map[father[x]][x]){ 92 best[x] = tmp; 93 } else best[x] = x; 94 return best[x]; 95 } 96 97 void Solve() 98 { 99 int i, j; 100 memset(vis, 0 , sizeof (vis)); 101 vis[S] = 1 ; 102 int m = 0 ; // 分支个数 103 mst = 0 ; // 最小生成树和 104 memset(father, - 1 , sizeof (father)); 105 memset(edg, 0 , sizeof (edg)); 106 for (i = 0 ; i < idx; i ++ ){ // 先求得m限制生成树 107 if ( ! vis[i]){ 108 m ++ ; 109 mst += prim(i); 110 } 111 } 112 /* for (i = 0; i < idx; i++){ 113 printf("%d----%d %d\n", father[i], i, map[i][father[i]]); 114 }可以用于调试错误 115 */ 116 int minadd, ax, bx,tmp; 117 int change; // 回路上权值最大的边,用于交换 118 for (i = m + 1 ; i <= k && i < idx; i ++ ){ 119 // 再由m度生成树得到m+1度生成树,最后求得k限制生成树 120 memset(best, - 1 , sizeof (best)); 121 for (j = 0 ; j < idx; j ++ ){ 122 if (best[j] == - 1 && father[j] != S){ 123 Best(j); 124 } 125 } 126 minadd = INF; // 交换边的最小差值 127 for (j = 0 ; j < idx; j ++ ){ // 遍历所有临边 128 if (map[S][j] != INF && father[j] != S){ 129 ax = best[j]; 130 bx = father[ax]; 131 tmp = map[S][j] - map[ax][bx]; 132 if (tmp < minadd){ 133 minadd = tmp; 134 change = j; 135 } 136 } 137 } 138 if (minadd >= 0 ) break ; // 用于度数不大于k的限制,如果k限制,就不用break了 139 mst += minadd; 140 ax = best[change]; 141 bx = father[ax]; 142 map[ax][bx] = map[bx][ax] = INF; 143 father[ax] = bx = S; // 改变生成树,将点ax直接指向源点S 144 map[ax][bx] = map[bx][ax] = map[change][S]; 145 map[S][change] = map[change][S] = INF; 146 } 147 } 148 int main() 149 { 150 int i, j, n, x, y, d; 151 char s1[NN], s2[NN]; 152 scanf( " %d " , & n); 153 for (i = 0 ; i <= NN - 2 ; i ++ ){ 154 for (j = 0 ; j <= NN - 2 ; j ++ ){ 155 map[i][j] = INF; 156 } 157 } 158 idx = 1 ; 159 strcpy(str[ 0 ], " Park " ); 160 while (n -- ){ 161 scanf( " %s%s%d " , s1, s2, & d); 162 x = find(s1); 163 if (x == - 1 ){ 164 strcpy(str[idx ++ ], s1); 165 x = idx - 1 ; 166 } 167 y = find(s2); 168 if (y == - 1 ){ 169 strcpy(str[idx ++ ], s2); 170 y = idx - 1 ; 171 } 172 if (d < map[x][y]){ 173 map[x][y] = d; 174 map[y][x] = d; 175 } 176 } 177 scanf( " %d " , & k); 178 S = 0 ; 179 Solve(); 180 printf( " Total miles driven: %d\n " , mst); 181 return 0 ; 182 } 183
复制代码

你可能感兴趣的:(最小度限制生成树)