• 图论:最小生成树


    Kruskal算法核心是加边,先把所有边按照权值从小到大排序,然后在剩下的所有没有被选过的边中,找到最小的边,如果和已经选取的边构成回路则放弃,选取次小边,直到选取了n-1条边为止,这样所有点就都连通了。

    每次从边集中选取的权值最小的边的两个顶点如果属于不同的树,就把他们合并(把这条边加入子图),反之则去看下一条边。

    n个点e条边的情况下,时间复杂度O(e^2),并查集优化之后O(eloge),适用于求稀疏图的最小生成树。

    int find(int x)
    {
        return p[x]==x?x:p[x]=find(p[x]);
    }

    这是自带路径压缩的并查集

    bool cmp(const int x,const int y)
    {
        return w[x]<w[y];
    }
        for(int i=1;i<=n;i++)
            p[i]=i;
        for(int i=1;i<=e;i++)
            r[i]=i;
        sort(r+1,r+e+1,cmp);

    第一个for循环是让每个点自己独自成为一个集合

    第二个for循环和sort是为了让所有边按照权重排序

    之后的内容就很显然了,完整的代码如下,程序读入了点数n和每两个点之间的边权关系,输出了最小生成树的结果。

     1 #include<iostream>
     2 #include<algorithm>
     3 using namespace std;
     4 const int maxn=105;
     5 const int maxm=10005;
     6 int n;
     7 long long ans=0;
     8 //
     9 int e=0;
    10 int u[maxm],v[maxm],w[maxm];
    11 int p[maxn],r[maxm];
    12 bool cmp(const int x,const int y)
    13 {
    14     return w[x]<w[y];
    15 }
    16 int find(int x)
    17 {
    18     return p[x]==x?x:p[x]=find(p[x]);
    19 }
    20 void kruskal()
    21 {
    22     for(int i=1;i<=n;i++)
    23         p[i]=i;
    24     for(int i=1;i<=e;i++)
    25         r[i]=i;
    26     sort(r+1,r+e+1,cmp);
    27     for(int i=1;i<=e;i++)
    28     {
    29         int m=r[i];
    30         int x=find(u[m]);
    31         int y=find(v[m]);
    32         if(x!=y)
    33         {
    34             ans+=w[m];
    35             p[x]=y;
    36         }
    37     }
    38 }
    39 int main()
    40 {
    41     cin>>n;
    42     for(int i=1;i<=n;i++)
    43     for(int j=1;j<=n;j++)
    44     {
    45         e++;
    46         u[e]=i,v[e]=j;
    47         cin>>w[e];
    48     }
    49     kruskal();
    50     cout<<ans<<endl;
    51     return 0;
    52 }

    接下来我们介绍Prim算法,规定一个点集和一个边集,初始状态下点集中包含任意一个点,边集为空

    然后重复一下操作直到点集中包含所有的点

    在边集中选取权值最小的一条边<u,v>,并且满足u在当前点集中而v在其补集中,如果有多条边满足这个条件就取任意一条

    将点v加入点集,<u,v>加入边集

    最后我们通过最终的边集和点集描述最小生成树

    对于时间复杂度来说,我写的好像是最第二种。。先粘在这里跑路了。。

    以后实现了相关细节再补充这部分内容,代码附下面。程序读入了n个点和m条边,输出了最小生成树的每一条边和边权之和。使用了邻接数组的数据结构。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 using namespace std;
      5 const int maxn=1005,maxm=1005;
      6 const int INF = 0x7fffffff;
      7 int n, m;
      8 int tot;
      9 struct point
     10 {
     11     bool vis;
     12     int t, w;
     13 };
     14 struct G
     15 {
     16     int cur;
     17     point e[maxm];
     18 }g[maxn];
     19 void addedge(int a,int b,int c)
     20 {
     21     tot++;
     22     point tmp;
     23     tmp.t = b;
     24     tmp.w = c;
     25     g[a].cur++;
     26     int t = g[a].cur;
     27     g[a].e[t] = tmp;
     28 }
     29 // 
     30 int vis[maxn];
     31 int fa[maxn];
     32 int dis[maxn];
     33 int tot1;
     34 int b1[maxn];
     35 int b2[maxn];
     36 int ans;
     37 //
     38 void prim()
     39 {
     40     memset(vis,0,sizeof(vis));
     41     int cnt=0;
     42     int p=1;
     43     for (int i=1;i<=n;i++)
     44         fa[i] = p;
     45     vis[p] = 1;
     46     cnt++;
     47     for(int i=1;i<=n;i++)
     48     {
     49         for (int tmp=1;tmp<=g[p].cur;tmp++)
     50         {
     51             if(g[p].e[tmp].t==i)
     52             dis[i]=g[p].e[tmp].w;
     53         }
     54         if(dis[i]==0)
     55             dis[i]=INF;
     56     }
     57     while(cnt!=n)
     58     {
     59         int l = INF;
     60         int q;
     61         for(int i=1;i<=n;i++)
     62         {
     63             if(l>dis[i]&&!vis[i])
     64             {
     65                 l=dis[i];
     66                 q=i;
     67             }
     68         }
     69         vis[q]=true;
     70         p=q;
     71         if(l!=INF)
     72         {
     73             tot1++;
     74             b1[tot1]=min(p,fa[p]);
     75             b2[tot1]=max(p,fa[p]);
     76         }
     77         cnt++;
     78         ans+=l;
     79         for(int i=1;i<=n;i++)
     80         if(!vis[i])
     81         {
     82             int t=INF;
     83             for (int tmp=1;tmp<=g[p].cur;tmp++)
     84             {
     85                 if(g[p].e[tmp].t==i)
     86                 t=g[p].e[tmp].w;
     87             }
     88             if(dis[i]>t)
     89             {
     90                 dis[i]=t;
     91                 fa[i]=p;
     92             }
     93         }
     94     }
     95 }
     96 int main()
     97 {
     98     cin>>n>>m;
     99     for(int i=1;i<=m;i++)
    100     {
    101         int a,b,c;
    102         cin>>a>>b>>c;
    103         addedge(a,b,c);
    104         addedge(b,a,c);
    105     }
    106     prim();
    107     cout<<tot1<<endl;
    108     for(int i=1;i<=tot1;i++)
    109         cout<<i<<":"<<b1[i]<<" "<<b2[i]<<endl;
    110     cout << ans << endl;
    111     return 0;
    112 }
  • 相关阅读:
    Linux忘了root的密码怎么办
    缩略图的实现
    ASP.NET程序编写注意 (转载)
    太极拳
    Linux系统管理技巧大荟萃
    茶经(转载)
    datagrid的显示控制
    太极功
    Linux下硬盘分区详解
    Tomcat4.0中文问题简单解决方法
  • 原文地址:https://www.cnblogs.com/aininot260/p/9272844.html
Copyright © 2020-2023  润新知