zoukankan      html  css  js  c++  java
  • 树的问题小结(最小生成树、次小生成树、最小树形图、LCA、最小支配集、最小点覆盖、最大独立集)

    树的定义:连通无回路的无向图是一棵树。

    有关树的问题:

    1、最小生成树。

    2、次小生成树。

    3、有向图的最小树形图。

    4、LCA(树上两点的最近公共祖先)。

    5、树的最小支配集、最小点覆盖、最大独立集。

     

    一、最小生成树

    解决的问题是:求无向图中边权值之和最小的生成树。

    算法有KruskalPrim

    Kruskal使用前向星和并查集实现,可以存储重边(平行边),时间复杂度是O(m log m  +  m)m是边的数量。

    Prim使用邻接矩阵建图,不可以存储重边(平行边),如果出现重边,存储的是权值最小的那一条,时间复杂度为O(n*n), n是顶点的数量。使用邻接表建图可能会提高效率。

    一般情况下,题目都是比较裸的。难度为易。

     模版:建好图以后,直接调用,输出。

    prim

     1 const int maxn=101,INF=0x3f3f3f3f;
     2 int dist[maxn],Map[maxn][maxn],pre[maxn];
     3 //向外延伸的最短边长,记录图信息,记录连接信息
     4 bool p[maxn];//1表示点已经在树的,0表示点在树外
     5 bool f[maxn][maxn];
     6 int Prim(int n)
     7 {
     8     int i,j,k,Min,ans=0;
     9     for(i=2;i<=n;i++)
    10     {
    11         p[i]=0;
    12         dist[i]=Map[1][i];
    13         pre[i]=1;
    14     }
    15     dist[1]=0;
    16     p[1]=1;
    17     for(i=1;i<n;i++)
    18     {
    19         Min=INF;
    20         k=0;
    21         for(j=1;j<=n;j++)
    22         {
    23             if(!p[j]&&dist[j]<Min)
    24             {
    25                 Min=dist[j];
    26                 k=j;
    27             }
    28         }
    29         if(k==0) return -1;//G不连通
    30         ans+=Min;
    31         p[k]=1;
    32         for(j=1;j<=n;j++)
    33         {
    34             if(!p[j]&&Map[k][j]!=INF&&dist[j]>Map[k][j])
    35             {
    36                 dist[j]=Map[k][j];
    37                 pre[j]=k;
    38             }
    39         }
    40     }
    41     return ans;
    42 }
    View Code

    Kruskal

     1 const int N=1010,M=100010;
     2 int f[N],r[N];
     3 struct node
     4 {
     5     int x, y, len;
     6 }e[M];
     7 int cmp(const node &a,const node &b)
     8 {
     9     return a.len<b.len;
    10 }
    11 void Make_Set(int n)
    12 {
    13     for(int i=0;i<=n;i++)
    14     {
    15         f[i]=i;
    16         r[i]=1;
    17     }
    18 }
    19 void Link(int a, int b)
    20 {
    21     if (r[a] > r[b]) {f[b] = a; r[a]+=r[b];}
    22     else {f[a] = b; r[b]+=r[a];}
    23 }
    24 int Find_Set(int a)
    25 {
    26     if (a == f[a]) return a;
    27     else return  f[a] = Find_Set(f[a]);
    28 }
    29 int Kruskal(int n,int m)
    30 {
    31     int i, j, k, s=0,cn=0;
    32     Make_Set(n);
    33     sort(e,e+m,cmp);
    34     for(i=0;i<m;i++)
    35     {
    36         j=Find_Set(e[i].x);
    37         k=Find_Set(e[i].y);
    38         if(j!=k) {
    39             Link(j,k);
    40             s+=e[i].len;
    41             cn++;
    42         }
    43         if(cn==n-1) break;
    44     }
    45     if(cn<n-1) return -1;
    46     return s;
    47 }
    View Code

    二、次小生成树

    1、最朴素的办法就是暴力枚举。求一遍最小生成树以后,依次删除最小生成树上的边,求此时的最小生成树,值最小的就是我们所求的。总共要运行 n次最小生成树算法,时间复杂度为n倍最小生成树算法的复杂度。有些题目能AC。暴力枚举在没有好方法时,也是一种非常好的方法。

    2、我们知道,添加任意一条不在最小生成树上的边以后一定会形成一个环。找到环上除了(u,v)以外的权值最大的边,把它删掉,那么此时生成树就可能是次小生成树。基于这种思想:首先求出原图最小生成树,权值之和为MST。在执行最小最小生成树算法的时候,记录任意两点(u,v)路径之间的最大权值,maxe[u][v]注意,不是记录以uv为起点和终点的边的最大值,是两点之间的路径,可能有多个点在它们之间。然后枚举每一条边,如果该边不是原本最小生成树上的边,就用MST - maxe[u][v] + Map[u][v],统计结果最小的。

    需要知道的是次小生成树的权值和可以与最小生成树的权值和相等。

    模版:

    prim(直接贴poj1679的代码了)

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 const int maxn=1100,INF=0x3f3f3f3f;
     8 int dist[maxn],Map[maxn][maxn],pre[maxn],maxe[maxn][maxn];
     9 //向外延伸的最短边长,记录图信息,记录连接信息,最长边
    10 bool vis[maxn];//1表示点已经在树的,0表示点在树外
    11 bool f[maxn][maxn];//存在边为1,用过为0
    12 int n,m;//顶点数目
    13 int Prim()
    14 {
    15     int i,j,k,Min,ans=0;
    16     memset(vis,0,sizeof(vis));
    17     memset(f,0,sizeof(f));
    18     memset(maxe,0,sizeof(maxe));
    19     for(i=2;i<=n;i++)
    20     {
    21         dist[i]=Map[1][i];
    22         pre[i]=1;
    23     }
    24     dist[1]=0;
    25     vis[1]=1;
    26     pre[1]=0;
    27     for(i=1;i<n;i++)
    28     {
    29         Min=INF;
    30         k=0;
    31         for(j=1;j<=n;j++)
    32         {
    33             if(!vis[j]&&dist[j]<Min)
    34             {
    35                 Min=dist[j];
    36                 k=j;
    37             }
    38         }
    39         if(Min==INF) return -1;//G不连通
    40         vis[k]=1;
    41         ans+=Min;
    42         f[pre[k]][k] = f[k][pre[k]]=1;
    43         for(j=1;j<=n;j++)
    44         {
    45             if(vis[j]) maxe[k][j]=maxe[j][k]=max(maxe[j][pre[k]],dist[k]);
    46             if(!vis[j]&&dist[j]>Map[k][j])
    47             {
    48                 dist[j]=Map[k][j];
    49                 pre[j]=k;
    50             }
    51         }
    52     }
    53     return ans;
    54 }
    55 void init()
    56 {
    57     for(int i=0;i<=n;i++)
    58         for(int j=0;j<=n;j++)
    59         {
    60             if(i==j)Map[i][j]=0;
    61             else Map[i][j]=INF;
    62         }
    63 }
    64 int main()
    65 {
    66     //freopen("test.txt","r",stdin);
    67     int i,j,k,a,b,c,ca;
    68     scanf("%d",&ca);
    69     while(ca--)
    70     {
    71         scanf("%d%d",&n,&m);
    72         init();
    73         for(i=0;i<m;i++)
    74         {
    75             scanf("%d%d%d",&a,&b,&c);
    76             Map[a][b]=Map[b][a]=c;
    77         }
    78         int ans=Prim();
    79         if(ans==-1) {printf("Not Unique!
    ");continue;}
    80         int res=INF;
    81         for(i=1;i<=n;i++)
    82         {
    83             for(j=i+1;j<=n;j++)
    84             {
    85                 if(!f[i][j]&&Map[i][j]!=INF)
    86                 {
    87                     res=(res,ans+Map[i][j]-maxe[i][j]);
    88                 }
    89             }
    90         }
    91         if(res==ans) printf("Not Unique!
    ");
    92         else printf("%d
    ",ans);
    93     }
    94     return 0;
    95 }
    View Code

    kruskal

     1 //O(mlogm+n^2)
     2 const int N=1010,M=100010,INF=0x3f3f3f3f;
     3 int f[N],r[N];
     4 int Find(int x)
     5 {
     6     if(x==f[x]) return x;
     7     return f[x]=Find(f[x]);
     8 }
     9 void Link(int x,int y)
    10 {
    11     int a=Find(x), b=Find(y);
    12     if(a!=b)
    13     {
    14         f[b]=a;
    15         r[a]+=r[b];
    16     }
    17 }
    18 struct node
    19 {
    20     int u,v,w;
    21     bool select;
    22 }edge[M];
    23 bool cmp(const node &a, const node &b)
    24 {
    25     if(a.w!=b.w) return a.w<b.w;
    26     if(a.u!=b.u) return a.u<b.u;
    27     return a.v<b.v;
    28 }
    29 struct node1
    30 {
    31     int to,next;
    32 }e[N];
    33 int cnt,tot,head[N],en[N],maxe[N][N];
    34 //e的边数、头结点、尾结点、两点在mst中的最大边
    35 void Kruskal(int n,int m)
    36 {
    37     int k=0, i,j,t;
    38     for(cnt=0;cnt<n;cnt++)
    39     {
    40         e[cnt].to=cnt+1;
    41         e[cnt].next=head[cnt+1];
    42         en[cnt+1]=cnt;
    43         head[cnt+1]=cnt;
    44     }
    45     sort(edge,edge+m,cmp);
    46     for(i=0;i<m;i++)
    47     {
    48         if(k==n-1) break;
    49         if(edge[i].w<0) continue;
    50         int a=Find(edge[i].u), b=Find(edge[i].v);
    51         if(a!=b)
    52         {
    53             for(j=head[a];j!=-1;j=e[j].next){
    54                 for(t=head[b];t!=-1;t=e[t].next){
    55                     int u=e[j].to, v=e[t].to;
    56                     maxe[u][v]=maxe[v][u]=edge[i].w;
    57                 }
    58             }
    59             e[en[b]].next=head[a];
    60             en[b]=en[a];
    61             Link(a,b);
    62             k++;
    63             edge[i].select=1;
    64         }
    65     }
    66 }
    67 void addedge(int u,int v,int w)
    68 {
    69     edge[tot].u=u;edge[tot].v=v;edge[tot].w=w;tot++;
    70 }
    71 void init()
    72 {
    73     tot=0;
    74     memset(head,-1,sizeof(head));
    75     memset(en,-1,sizeof(en));
    76     for(int i=0;i<N;i++){
    77         edge[i].select=0;
    78         f[i]=i;
    79         r[i]=1;
    80     }
    81 }
    82 int main()
    83 {
    84     //freopen("test.txt","r",stdin);
    85     int mst,secmst;
    86     Kruskal(n,m);
    87     mst=0;
    88     for(i=0;i<m;i++){
    89         if(edge[i].select) mst+=edge[i].w;
    90     }
    91     secmst=INF;
    92     for(i=0;i<m;i++){
    93         if(!edge[i].select)
    94             secmst=min(secmst,mst+edge[i].w-maxe[edge[i].u][edge[i].u]]);
    95     }
    96 
    97     return 0;
    98 }
    View Code

    算法的不同,当然也继承了该算法的特点。Prim不能存重边。这是需要注意的。

    题目:Hdu2489  poj1679

    hdu4081http://www.cnblogs.com/Potato-lover/p/3938482.html 

     

    三、有向图的最小树形图

    解决的问题:一水源给菜地供水,水只能从高处向低处流,修水渠需要一定的花费,问怎样才能使得花费最低。

    边是有方向的,与最小生成树不同,这可以理解为有向图的最小生成树。

    算法是朱刘算法,难得的以国人名字命名的算法。

    邻接矩阵建图,时间复杂度为O( n*n*n )。 hdu4009用这种方法超时了。

    前向星建图,时间复杂度为O( n*m )

    朱刘算法的两种实现可以看我的博客 http://www.cnblogs.com/Potato-lover/p/3942321.html

    题目:hdu4009 、 hdu2121

     

    四、LCA(树上两点的最近公共祖先)

    解决的问题:求树上的任意两个点之间的最短距离。

    用最短路的方法是行不通的,复杂度太高。所以才有解决LCA的专门的算法。

    我只会离线的Tarjan(图拉)算法。 时间复杂度为O(m+q),m是边数,q是问题的个数。

    我的博客里已经有相关的介绍了。http://www.cnblogs.com/Potato-lover/category/613545.html

    题目:poj1330 (hdu2586) 

     

    五、树的最小支配集、最小点覆盖、最大独立集

    注意:是树上的,图中一定不能有圈。

    都是基于深度优先搜索。

    最小支配集:对于图G=(V,E),从V取尽量少的点组成一个集合,使得V中剩余的点都与取出来的点有边相连。

    最小点覆盖:对于图G=(V,E),从V取尽量少的点组成一个集合,使得E中所有的边都与取出来的点相连。

    最大独立集:对于图G=(V,E),从V取尽量多的点组成一个集合,使得这些点之间没有边相连。

    时间复杂度都是O(n)。

    这方面的题目似乎不曾出现,但是作为图论的学习者,有必须会。

     实现的算法很相似。

    顶点下标从1开始的。

      1 /*newpos[i]表示深度优先遍历序列的第i个点是哪个点,
      2 now表示当前深度优先遍历序列已经有多少个点了。select[]用于
      3 深度优先遍历的判重,p[i]表示点i的父节点的编号。对于greedy(),
      4 s[i]为1表示第i个点被覆盖。se[i]表示点i属于要求的点集*/
      5 
      6 const int N = 1010;
      7 struct node
      8 {
      9     int to, w, next;
     10 }e[N*N];
     11 int head[N],tot,now;
     12 int p[N], newpos[N] ;
     13 bool select[N];
     14 bool s[N],se[N];
     15 //深度优先遍历,得到深度优先遍历序列
     16 void dfs(int x)
     17 {
     18     newpos[now++] = x;
     19     int k;
     20     for(k=head[x]; k!=-1; k=e[k].next)
     21     {
     22         if(!select[e[k].to])
     23         {
     24             select[e[k].to]= 1;
     25             p[e[k].to]=x;
     26             dfs(e[k].to);
     27         }
     28     }
     29 }
     30 //最小支配集
     31 int greedy1()
     32 {
     33     memset(s,0,sizeof(s));
     34     memset(se,0,sizeof(se));
     35     int ans=0;
     36     int i;
     37     for(i=now-1; i>=0; i--)
     38     {
     39         int t=newpos[i];
     40         if(!s[t])
     41         {
     42             if(!se[p[t]])
     43             {
     44                 se[p[t]]=1;
     45                 ans++;
     46             }
     47             s[t] = 1;
     48             s[p[t]] = 1;
     49             s[p[p[t]]] = 1;
     50         }
     51     }
     52     return ans;
     53 }
     54 //最小点覆盖
     55 int greedy2()
     56 {
     57     memset(s,0,sizeof(s));
     58     memset(se,0,sizeof(se));
     59     int ans=0;
     60     int i;
     61     for(i=now-1; i>=1; i--)
     62     {
     63         int t=newpos[i];
     64         if(!s[t]&&!s[p[t]])
     65         {
     66             se[p[t]]=1;
     67             ans++;
     68             s[t] = 1;
     69             s[p[t]] = 1;
     70         }
     71     }
     72     return ans;
     73 }
     74 //最大独立集
     75 int greedy3()
     76 {
     77     memset(s,0,sizeof(s));
     78     memset(se,0,sizeof(se));
     79     int ans=0;
     80     int i;
     81     for(i=now-1; i>=0; i--)
     82     {
     83         int t=newpos[i];
     84         if(!s[t])
     85         {
     86             se[t]=1;
     87             ans++;
     88             s[t] = 1;
     89             s[p[t]] = 1;
     90         }
     91     }
     92     return ans;
     93 }
     94 void addedge(int i,int j)
     95 {
     96     e[tot].to=j;e[tot].next=head[i];head[i]=tot++;
     97 }
     98 void init()
     99 {
    100     tot=now=0;
    101     memset(head,-1,sizeof(head));
    102     memset(select, 0, sizeof(select));
    103 }
    104 int main()
    105 {
    106     /* 读入图*/
    107     select[1]=1;
    108     p[1]=1;
    109     dfs(1);
    110     printf("%d
    ",greedy());
    111     return 0;
    112 }
    View Code
  • 相关阅读:
    [转]ArcSDE数据被锁定后的解锁方法
    一个ArcGIS网络分析的最短路径例子||A Network Analyst Shortest Route of ArcGIS[转]
    Flex项目调试及日志记录
    组件服务计算机我的电脑右键无属性,组件服务打不开
    ORACLE纯SQL实现多行合并一行[转]
    自定义组件开发 第二节 MXML组件开发
    FLEX中使用BitmapFill的source属性指定SVG类文件
    FLEX动态创建事件
    Sublime Text3常用插件推荐
    RemoveView在android2.1以及之后才支持addView
  • 原文地址:https://www.cnblogs.com/Potato-lover/p/3997009.html
Copyright © 2011-2022 走看看