几种特殊的生成树

几种特殊的生成树

分类: ACM•图论   399人阅读  评论(0)  收藏  举报

1.(严格)次小生成树

解法:1.依次删除树上的边后求最小生成树,判生成树唯一时只需删存在与它长度相同的边,严格次小生成树只需删没有与自己长度相同的边。

            2.以最小生成树上每个点为根dfs,求出树上任意两点之间的最长边。枚举所有不在最小生成树上的边,将其添加到树上必定形成一个环,去掉环上的最长边形成的生成树最小。判断生成树是否唯一,求次小生成树严格次小生成树等均在此处处理。

[java]  view plain copy print ?
  1. void work(){  
  2.         best=new int[n][n];  
  3.         vis=new boolean[n];  
  4.         for(int i=0;i<n;i++){  
  5.             Arrays.fill(vis, false);  
  6.             dfs(i,i);  
  7.             }  
  8.         int min=99999999;  
  9.         for(int i=0;i<m;i++)  
  10.             if(!ed[i].flag)  
  11.                 if(mst+ed[i].v-best[ed[i].a][ed[i].b]<min)  
  12.                     min=mst+ed[i].v-best[ed[i].a][ed[i].b];   
  13.     }  
  14.     void dfs(int v,int a){  
  15.         vis[a]=true;  
  16.         for(int i=E[a];i!=-1;i=buf[i].ne){  
  17.             int b=buf[i].be;  
  18.             if(!vis[b]){  
  19.                 best[v][b]=Math.max(best[v][a], buf[i].dis);  
  20.                 dfs(v,b);  
  21.             }  
  22.         }  
  23.     }  

题目:Ural 1416. Confidential 要求最小生成树和次小生成树,注意判连通性。

      Poj 1679 The Unique MST判断最小生成树是否唯一


2.最小度限制生成树

 把顶点V的度数<= K 称做度限制条件,把满足这一条件的生成树叫做度限制生成树,把权值和最小的度限制生成树称最小度限制生成树

解法:1.把顶点去除,对其他点边使用kruskal求生成树,并求出共有m个连通分量。

      2.一次添加顶点到各个连通分量的最短边,得到m度最小生成树,若m>k则不存在k度生成树。

      3.dfs求出m度生成树上顶点到各个点间路径上的最长边best[i]。

      4.依次次遍历从顶点出发的为添加到m度最小生成树的边,求出best[ed[j].a] - ed[j].dis>0且最大的一条边,添加到k度生成树中。

      5.重复4知道m==k或不存在那样一条边。

题目:poj 1639 Picnic Planning 


3.最优比例生成树

一个带权完全图,每条边都有自己的花费值cost[i]和 长度dis[i],求一个生成树,使得r=(∑cost[i]*x[i] ) / (∑dis[i]*x[i] )最小。

解法:详见《0-1分数规划问题》

题目:poj2526 Desert King


4.最小树形图

有向图的最小生成树,并且规定了起点。

解法:1.首先dfs判断一下起点可达其他任意点,否则不存在树形图。

      2.为每个点找一条最小的入边,如果没环那么这些边就构成了最小树形图,转入4;否则转入3.

      3.将环上每条边的边权加入到ans中,同时形成新的点new,对于环内多有的点i,如果存在边<j,i>则<j,new>的边权等于所有 <j,i>-<pre[i],i>中最小的(因为缩点后再次构图必须从环中去除一条边<pre[i],i>再添加一条最小边<x,i>,这样就可以保证答案的正确性,很巧妙,换个图就很清晰了),<new,j>的边权=所有<i,j>的最小值,缩点完成,转向2.

      4.缩点后n个点,n-1条边,切无环,此时就是一颗连通树了,ans+=这n-1条边的边权记得答案;


以上是国人发明的“朱刘算法”,邻接矩阵复杂度(n ^3)临界表复杂度(VE)。

对于跟不固定的情况,wy教主有一巧妙方法,今后再去实现。摘录如下:
新加一个点,和每个点连权相同的边,这个权大于原图所有边权的和,这样这个图固定跟的最小树形图和原图不固定跟的最小树形图就是对应的了。

题目:Poj3164 Command Network

[java]  view plain copy print ?
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4. import java.io.StreamTokenizer;  
  5. import java.util.Arrays;  
  6.   
  7. public class Command3164 {  
  8.     class point {  
  9.         int x, y;  
  10.         double dis(point o) {  
  11.             double temp = (x - o.x) * (x - o.x) + (y - o.y) * (y - o.y);  
  12.             return Math.sqrt(temp);  
  13.         }  
  14.     }  
  15.     double map[][] = new double[110][110], inf = 99999999.99;  
  16.     int pre[] = new int[110];  
  17.     boolean vis[] = new boolean[110], is[] = new boolean[110];  
  18.   
  19.     void build() {  
  20.         for (int i = 2; i <= n; i++)  
  21.             if (!vis[i]) {  
  22.                 pre[i] = 1;  
  23.                 for (int j = 2; j <= n; j++)  
  24.                     if (i != j && !vis[j] && map[pre[i]][i] > map[j][i])  
  25.                         pre[i] = j;  
  26.             }  
  27.     }  
  28.   
  29.     boolean bcc() {  
  30.         for (int i = 2; i <= n; i++) {  
  31.             if (vis[i])  
  32.                 continue;  
  33.             Arrays.fill(is, false);  
  34.             int j;  
  35.             for (j = i; j != 1 && !is[j]; j = pre[j])  
  36.                 is[j] = true;  
  37.             if (j == 1)  
  38.                 continue;// j存在自环,环内所有点所为j  
  39.             ans += map[pre[j]][j];  
  40.             for (i= pre[j]; i!= j; i = pre[i]) {  
  41.                 ans += map[pre[i]][i];  
  42.                 vis[i] = true;  
  43.             }//缩点  
  44.             for (int k = 1; k <= n; k++)  
  45.                 if (!vis[k] && map[k][j] < inf)  
  46.                     map[k][j] -= map[pre[j]][j];  
  47.             for (i = pre[j]; i!= j; i = pre[i])// 处理环内所有点的边  
  48.                 for (int k = 1; k <= n; k++)  
  49.                     if (!vis[k]) {  
  50.                         map[j][k] = Math.min(map[j][k], map[i][k]);// 出边  
  51.                         if (map[k][i] < inf)  
  52.                             map[k][j] = Math.min(map[k][j], map[k][i]// 入边  
  53.                                     - map[pre[i]][i]);  
  54.                     }  
  55.             return true;  
  56.         }  
  57.         return false;  
  58.     }  
  59.   
  60.     void dfs(int x) {  
  61.         vis[x] = true;  
  62.         cnt++;  
  63.         for (int i = 1; i <= n; i++)  
  64.             if (!vis[i] && map[x][i] < inf)  
  65.                 dfs(i);  
  66.     }  
  67.   
  68.     int cnt;  
  69.     void work() {  
  70.         cnt = 0;  
  71.         Arrays.fill(vis, false);  
  72.         dfs(1);  
  73.         if (cnt < n) {  
  74.             System.out.println("poor snoopy");  
  75.             return;  
  76.         }  
  77.         Arrays.fill(vis, false);  
  78.         while (true) {  
  79.             build();  
  80.             if (!bcc())  
  81.                 break;  
  82.         }  
  83.         for (int i = 2; i <= n; i++)  
  84.             if (!vis[i])  
  85.                 ans += map[pre[i]][i];  
  86.         System.out.println(String.format("%.2f", ans));  
  87.     }  
  88.   
  89.     void init() throws IOException {  
  90.         ans = 0;  
  91.         for (int i = 1; i <= n; i++) {  
  92.             Arrays.fill(map[i], inf);  
  93.             pp[i] = new point();  
  94.             pp[i].x = next();  
  95.             pp[i].y = next();  
  96.         }  
  97.         int a, b;  
  98.         while (m-- > 0) {  
  99.             a = next();  
  100.             b = next();  
  101.             map[a][b] = Math.min(map[a][b], pp[b].dis(pp[a]));  
  102.         }  
  103.     }  
  104.   
  105.     point pp[] = new point[110];  
  106.     int n, m;  
  107.     double ans;  
  108.     StreamTokenizer in = new StreamTokenizer(new BufferedReader(  
  109.             new InputStreamReader(System.in)));  
  110.     int next() throws IOException{  
  111.         in.nextToken();  
  112.         return (int)in.nval;  
  113.     }  
  114.   
  115.     void run() throws IOException {  
  116.         while (in.nextToken()!=in.TT_EOF) {  
  117.             n = (int)in.nval;  
  118.             m =next();  
  119.             init();  
  120.             work();  
  121.         }  
  122.     }  
  123.   
  124.     public static void main(String[] args) throws IOException {  
  125.         new Command3164().run();  
  126.     }  
  127. }  
 

POJ 1639 Picnic Planning【度限制最小生成树】

分类: 【图论专辑】   791人阅读  评论(3)  收藏  举报

/*  第一道度限制生成树,写的比较崩溃@@
 *  度限制生成树是指对生成树中某些点限制度的生成树,如果是要求权值最小的话
 *  便是度限制最小生成树。一般题目也就是对某一个点进行度限制,因为所有点进行
 *  度限制是NP问题。
 *  解法:1)先去掉有度限制的点和与它相邻的边,对求剩下的点求最小生成树。由于剩下的点可能不连同,所以
 *                 要对每一个连通分量求最小生成树。
 *              2)然后对每一个连通分量,从中找出一条距原点(有度限制的点)最短的一条边加进去。如果有M个连通分量,
 *                 那么现在求得的便是一个M度最小生成树。
 *              3)设度限制为K > M,那么再进行 K-M 次循环,每次进行如下操作:对每一个不在生成树中的点,连一条到原点的
 *                  边,那么现在一定构成了一个环。然后找出这个环中除了新连的边外最长的边,和新添加的边作差。
 *                 如果结果是负值,找到最小的这个值,更新,如此进行K-M 次操作。
 *                 由于该题求的是不超过K度的生成树,所以如果无法更新便可直接跳出循环
 *             PS:以上便是度限制生成树的基本算法,个人感觉其中关键部分一个是对每一个连通分量求最小生成树,另一个便是添边然后找环
 *                      操作,网上的大部分解法都很复杂,代码动辄200+  今天请教了Ych,他教了我一个好方法,可以这样:由于去掉原点后图可能
 *                      不连通,而对每一个连通分量求最小生成树又很烦,所以可以初始化时将所有边初始化为无穷大,然后对去掉原点的图只进行一次
 *                      求最小生成树的操作。这样如果图不连通的话那么会自动加一条无穷大的边,但是没关系。接下来在从生成树中找到一个距原点
 *                      最近的点加到生成树中,此时是一棵1度生成树(不是最小生成树),然后进行K-1次上述的更新。其中找环可以用DFS,也可以用
 *                      BFS。我选择了BFS,因为更直观。
 *            最后有一点一定要注意就是对某些变量赋值时的无穷大一定要比边初始化的那个无穷大还要大,否则会出错。今天因为这个错误调试
 *            了近两个小时。。。崩溃。。。
 */

[cpp]  view plain copy
  1. #include <cstdio>  
  2. #include <iostream>  
  3. #include <cstring>  
  4. #include <algorithm>  
  5. #include <map>  
  6. #include <queue>  
  7. #include <string>  
  8. #define min(a,b)  ((a)<(b)?(a):(b))  
  9. #define INF (1<<30)  
  10. #define MAXN 25  
  11. using namespace std;  
  12. int cost[MAXN][MAXN],pre[MAXN],dis[MAXN];  
  13. bool vis[MAXN],tree[MAXN][MAXN];    
  14. int M,N,K,ans;  
  15. inline bool Input(){  
  16.     char s[20],e[20];  
  17.     int w,cont = 1;  
  18.     map<string,int>f;  
  19.     f.clear();  
  20.     f["Park"] = 0;  
  21.     memset(cost,0x3f,sizeof(cost));  
  22.     while(M--){  
  23.         scanf("%s %s %d",s,e,&w);  
  24.         if(f.find((string)s) == f.end())  
  25.             f[(string)s] = cont++;  
  26.         if(f.find((string)e) == f.end())  
  27.             f[(string)e] = cont++;  
  28.         int u = f[(string)s],v = f[(string)e];  
  29.         cost[u][v] = cost[v][u] = w;  
  30.     }  
  31.     N = cont;  
  32.     scanf("%d",&K);  
  33.     return true;  
  34. }  
  35. inline int Find_max(int s,int e,int &st,int &ed){ // BFS找到环上边权最大的边  
  36.     int que[MAXN+10];  
  37.     memset(vis,false,sizeof(vis));  
  38.     vis[s] = true;  
  39.     int head = 0,tail = 0;  
  40.     pre[s] = -1;  
  41.     que[tail++] = s;  
  42.     while(tail > head){  
  43.         int tep = que[head++];  
  44.         for(int i = 1;i < N; ++i){  
  45.             if(!vis[i] && tree[tep][i]){  
  46.                 vis[i] = true;  
  47.                 pre[i] = tep;     
  48.                 que[tail++] = i;  
  49.             }  
  50.         }     
  51.     }  
  52.     int re = -1;  
  53.     while(s != e){  
  54.         if(re < cost[e][pre[e]]){  
  55.             re = cost[e][pre[e]];  
  56.             st = e;  
  57.             ed = pre[e];  
  58.         }  
  59.         e = pre[e];  
  60.     }  
  61.     return re;  
  62. }  
  63. inline int prim(){  
  64.     memset(vis,false,sizeof(vis));  
  65.     memset(tree,false,sizeof(tree));  
  66.     for(int i = 1;i < N; ++i){  
  67.         pre[i] = 1;  
  68.         dis[i] = cost[1][i];  
  69.     }  
  70.     vis[1] = true;  
  71.     int sum = 0;  
  72.     for(int i = 1;i < N-1; ++i){  
  73.         int md = INF,idex = -1;  
  74.         for(int j = 1;j < N ; ++j){  
  75.             if(!vis[j] && dis[j] < md){  
  76.                 md = dis[j];  
  77.                 idex = j;     
  78.             }  
  79.         }  
  80.         if(idex == -1) break;  
  81.         vis[idex] = true;  
  82.         tree[idex][pre[idex]] = tree[pre[idex]][idex] = true;  
  83.         sum += md;  
  84.         for(int j = 1;j < N; ++j)  
  85.             if(!vis[j] && dis[j] > cost[idex][j]){  
  86.                 dis[j] = cost[idex][j];  
  87.                 pre[j] = idex;  
  88.             }  
  89.     }  
  90.     return sum;  
  91. }  
  92. inline void solve(){  
  93.     ans = prim();          //先对剩余点求最小生成树  
  94.     int m = INF,idex = -1;  
  95.     for(int i = 1;i < N; ++i){    // 然后从生成树中找一个距度限制点最近的点加进来  
  96.         if(cost[i][0] < m){  
  97.             m = cost[i][0];  
  98.             idex = i;  
  99.         }  
  100.     }  
  101.     ans += m;  
  102.     tree[idex][0] = tree[0][idex] = true;  
  103.     for(int i = 1;i < K; ++i){     //然后进行K-1次循环每次进行树的更新  
  104.         int st,ed,s,e;  
  105.         idex = -1; m = 0;  
  106.         for(int j = 1,k;j < N; ++j){  
  107.             if(!tree[j][0]){      
  108.                 k = Find_max(0,j,st,ed);  
  109.                 if(m > cost[j][0] - k){  
  110.                     m = cost[j][0] - k;  
  111.                     idex = j;  
  112.                     s = st;  
  113.                     e = ed;  
  114.                 }  
  115.             }  
  116.         }  
  117.         if(idex == -1) break;  
  118.         ans += m;  
  119.         tree[idex][0] = tree[0][idex] = true;  
  120.         tree[s][e] = tree[e][s] = false;  
  121.     }  
  122.     printf("Total miles driven: %d/n",ans);  
  123. }  
  124. int main()  
  125. {  
  126.     while(scanf("%d",&M) != EOF){  
  127.         Input();  
  128.         solve();  
  129.     }  
  130.     return 0;  
  131. }  


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值