求最小生成树的两种方法

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 }

原文地址:https://www.cnblogs.com/aininot260/p/9272844.html

时间: 2024-08-30 02:08:52

求最小生成树的两种方法的相关文章

除法求模中求逆元的两种方法

今天下午还是有点闲的,不想刷题,不想补题,突然想起昨天的training 3里I题涉及到除法取模的问题,就来总结一下 首先对于模运算来说,是没有对于除法的取模的(即没有(a/b)%mod==a%mod/b%mod),但是在很多题目中都涉及到除法取模,所以就必须要了解或者掌握,对于除法取模以(a/b)%mod来说,我们首先需要得到b的逆元,根据逆元的定理 对于正整数和,如果有,那么把这个同余方程中的最小正整数解叫做模的逆元. 然后就是求逆元的两种方法. 第一种方法就是比较普遍的,也是挺基础的,就是

递归很耗内存+多项式求值的两种方法+c语言计时方法

1.用for循环写一个函数,实现从1开始输出到N的正整数. 有两宗实现方法,一种是递归,另一种是非递归 //非递归 void PrintN1(int N){ int i; for(i=1;i<=N;i++){ printf("%d\n",i); } return; } //递归 递归对空间的需求很大,当数字很大的时候,需要很大的内存,当数字是十万的时候递归就崩了 void PrintN2(int N){ if(N){ PrintN2(N-1); printf("%d\n

用c#求质数的两种方法

求1到100的中的质素 方法1: Console.Write("1到100中的质数有:");   for(int i=1;i<=100;i++){ int m=0; for(int j=1;j<=i;j++){ if(i%j==0){ m++; } } if(m==1){ Console.Write(i+"  "); }else if(m==2){ Console.Write(i+"  "); } } 方法2: Console.Wri

求质数的两种方法1-100

// 1-100以内质数的和 for (int i = 1; i <= 100; i++) { boolean b = true; if (i != 1) { for (int j = 2; j < i; j++) { if (i % j == 0) { b = false; break; } } if (b) { System.out.println(i); } } } System.out.println("************************************

两种方法求丑数

我们把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 方法1 : 暴力破解,逐个判断 代码: <pre name="code" class="cpp">#include <iostream> #include <vector> using namespace std; //判断是否是丑数 bool isUgly(int index){ while(index % 2

HDU 1013 Digital Roots(两种方法,求数字根)

Digital Roots Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 67949    Accepted Submission(s): 21237 Problem Description The digital root of a positive integer is found by summing the digits of

利用颜色和形态学两种方法进行车牌区域提取的OpenCV代码

要想提取车牌号,首先你要定位车牌区域嘛,本文分别两种方法用,即颜色和形态学的方法,对车牌区域进行判定.说得是两种方法,其实两种方法并无多大的区别,只是有一步的判断标准不一样而已,你看了下面整理出的的思路就知道两者的区别真的很小了. 方法一:利用颜色提取车牌区域的思路: ①求得原图像的sobel边缘sobelMat ②在HSV空间内利用车牌颜色阈值对图像进行二值化处理,得到图像bw_blue→ ③由下面的判别标准得到图像bw_blue_edge for (int k = 1; k != heigh

hdu1828 Picture(线段树+离散化+扫描线)两种方法

C - Picture Time Limit:2000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u Submit Status Description A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or

最小生成树的两种算法:Prim和Kruskal算法

越来越明白了一个道理:你写不出代码的原因只有一个,那就是你没有彻底理解这个算法的思想!! 以前写过最小生成树,但是,水了几道题后,过了一段时间,就会忘却,一点也写不出来了.也许原因只有一个,那就是我没有彻底理解这两种算法. 主题: 其实,求最小生成树有两个要点,一个是权值最小,还有一个就是这个图必须是树.而Prim和Kruskal的不同之处在于两者选择的变量不同,Prim选择的是始终保持权值最小,然后逐个加点构建一棵树.而Kruskal则是始终保证是一棵树(虽然构建过程中不一定是真正的树,但并查