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
  • 相关阅读:
    Educational Codeforces Round 86 (Rated for Div. 2) D. Multiple Testcases
    Educational Codeforces Round 86 (Rated for Div. 2) C. Yet Another Counting Problem
    HDU
    HDU
    HDU
    HDU
    Good Bye 2019 C. Make Good (异或的使用)
    Educational Codeforces Round 78 (Rated for Div. 2) C. Berry Jam
    codeforces 909C. Python Indentation
    codeforces1054 C. Candies Distribution
  • 原文地址:https://www.cnblogs.com/Potato-lover/p/3997009.html
Copyright © 2011-2022 走看看