生成树

Kruskal算法

  近期内写的比较多的一种算法(对于Prim基本没有接触)  

  Kruskal是一种贪心算法,充分利用并查集思想,将边按照边权排序,每次在剩下的边集中选择权值最小且端点不在同一集合的边加入生成树中,直到加到第N-1条边;

  算法的核心在于判断新加入的边会不会让图G‘产生环,如果存在环,舍弃这条边。

P3366【模板】最小生成树 https://www.luogu.org/problemnew/show/P3366

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxx=500010;
 7 int n,m,ans,cnt,l;
 8 int fa[maxx];
 9 
10 struct haaa{
11     int x,y,val;
12 }e[maxx<<1];
13 
14 bool cmp(haaa aa,haaa bb){
15     return aa.val<bb.val;
16 }
17 
18 int find(int x){//寻找父亲 
19     if(fa[x]==x) return x;
20     else return fa[x]=find(fa[x]);
21 }
22 
23 int main(){
24     scanf("%d%d",&n,&m);
25     for(int i=1;i<=n;i++) fa[i]=i;
26     for(int i=1;i<=m;i++){
27         int xx,yy,w;
28         scanf("%d%d%d",&xx,&yy,&w);
29         e[++cnt].x=xx;e[cnt].y=yy;e[cnt].val=w;
30         //加边 
31     }
32     sort(e+1,e+1+cnt,cmp);
33     for(int i=1;i<=cnt;i++){
34         int u=find(e[i].x),v=find(e[i].y);
35         if(u==v) continue;
36         fa[u]=v;l++;
37         ans+=e[i].val;
38         if(l==n-1) break;
39     }
40     printf("%d",ans);
41 } 

 

P1536 村村通 https://www.luogu.org/problemnew/show/P1536

  这道题的主要思路是并查集,但其实去看代码核心,跟Kru还是有非常多的相似的(不过在洛谷的……上出现了,我们就写一下吧)

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 const int maxx=10010;
 6 int n,m,x,y,ans,u,v;
 7 int f[maxx];
 8 
 9 int find(int x){
10     if(f[x]==x) return x;
11     else return f[x]=find(f[x]);
12 }
13 
14 int main(){
15     while(cin>>n>>m){
16         ans=n-1;
17         for(int i=1;i<=n;i++) f[i]=i;
18         for(int i=1;i<=m;i++) {
19             scanf("%d%d",&x,&y);
20             u=find(x);v=find(y);
21             if(u==v) continue;
22             f[u]=v;ans--;
23         }
24         printf("%d\n",ans);
25     }
26     return 0; 
27 }

P1546 最短网络 https://www.luogu.org/problemnew/show/P1546

P2330 繁忙的都市 https://www.luogu.org/problemnew/show/P2330

P2504 聪明的猴子 https://www.luogu.org/problemnew/show/P2504

P1195 口袋的天空 https://www.luogu.org/recordnew/show/13245050

P2212 浇地 https://www.luogu.org/problemnew/show/P2212

  //对于题目要求处理的条件,一定要处理好

  挺裸的最小生成树板子

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 const int maxx=2000002;
 8 int n,tmp,ha=0;
 9 int f[maxx]; 
10 int ans=0,p=1;
11 
12 struct nod{
13     int x,y,w;
14 }a[maxx];
15 
16 bool cmp(nod a,nod b){
17     return a.w<b.w;
18 }
19 
20 int find(int x){
21     if(x==f[x]) return x;
22     f[x]=find(f[x]);
23     return f[x];
24 }
25 
26 int main(){
27     scanf("%d",&n);
28     for(int i=1;i<=n;i++){
29         f[i]=i;//初始化 
30         for(int j=1;j<=n;j++){
31             scanf("%d",&tmp);
32             if(i<j){
33                 ha++;
34                 a[ha].x=i;a[ha].y=j;a[ha].w=tmp;//加边 
35             }
36         }
37     }
38     sort(a+1,a+1+ha,cmp);
39     for(int i=1;i<=ha;i++){
40         if(find(a[i].x)!=find(a[i].y)){
41             ans+=a[i].w;
42             f[find(a[i].x)]=a[i].y;
43             p++;//设置边界条件 
44             if(p==n) break;
45         }
46     }
47     printf("%d",ans);
48     return 0;
49 }

P1991 无线通讯网 https://www.luogu.org/problemnew/show/P1991

  这道题是求瓶颈生成树(无向图G的一颗瓶颈生成树(bottleneck spanning tree)T是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。瓶颈生成树的值为T中最大权值边的权。  无向图的最小生成树一定是瓶颈生成树,但瓶颈生成树不一定是最小生成树。)

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxx=100010;
int s,p,tot,cnt,l;
//S 表示可安装的卫星电话的哨所数,P 表示边防哨所的数量
int f[maxx];
int xx[maxx],yy[maxx];
double xz[maxx],ans=0;

int find(int sa){
    if(f[sa]==sa) return sa;
    else return f[sa]=find(f[sa]);
}

struct haaa{
    int x,y;
    double val;
}e[maxx<<1];

bool cmp(haaa aa,haaa bb){
    return aa.val<bb.val;
}

int main(){
    scanf("%d%d",&s,&p);
    for(int i=1;i<=p;i++) f[i]=i;
    for(int i=1;i<=p;i++) scanf("%d%d",&xx[i],&yy[i]);
    for(int i=1;i<=p;i++)
    {
        for(int j=i+1;j<=p;j++){
            e[++cnt].x=i,e[cnt].y=j;
            e[cnt].val=sqrt(double((xx[i]-xx[j])*(xx[i]-xx[j])+(yy[i]-yy[j])*(yy[i]-yy[j])));
            //double 和 int 的类型一定要处理好 
        }
    }
    sort(e+1,e+cnt+1,cmp);
    for(int i=1;i<=cnt;i++){
        int u=find(e[i].x),v=find(e[i].y);
        if(u==v) continue;
        f[u]=v;l++;
        ans=max(ans,e[i].val);
        if(l==p-s) break;
        //考虑题目中给出的,p和s的关系 
    }
    printf("%.2lf",ans);
    return 0;
}

 P4047 部落划分 https://www.luogu.org/problemnew/show/P4047

 1 /*其实开始对这道题的理解是并查集的,每次在两个集合分别加入
 2 边权值最小的边,到最后处理两个集合里的最小距离
 3 tj:最小生成树,找n-k条边,第n-k+1边就是答案*/
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<iostream>
 7 #include<algorithm>
 8 using namespace std;
 9 
10 const int maxx=1000010;
11 int n,k;
12 int l,vis,cnt;
13 int x[maxx],y[maxx],f[maxx];
14 double jl;
15 
16 struct haaa{
17     int x,y;
18     double val;
19 }e[maxx<<1];
20 
21 bool cmp(haaa aa,haaa bb){
22     return aa.val<bb.val;
23 }
24 
25 void add(int xx,int yy,double jll){
26     e[++cnt].x=xx;
27     e[cnt].y=yy;
28     e[cnt].val=jll;
29 }
30 
31 int find(int sa){
32     if(f[sa]==sa) return sa;
33     else return f[sa]=find(f[sa]);
34 }
35 
36 void kru(){
37     for(int i=1;i<=cnt;i++){
38         int u=find(e[i].x),v=find(e[i].y);
39         if(u!=v){
40             f[u]=v;
41             l++;
42             if(l==n-k+1){
43                 printf("%.2lf",e[i].val);
44                 return;
45             }
46         }
47     }
48 }
49 
50 int main(){
51     scanf("%d%d",&n,&k);    
52     for(int i=1;i<=n;i++) f[i]=i;
53     for(int i=1;i<=n;i++){
54         scanf("%d%d",&x[i],&y[i]);
55     }
56     for(int i=1;i<=n;i++)
57         for(int j=i+1;j<=n;j++){//!!!
58             double s=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
59             add(i,j,s);
60         }
61     sort(e+1,e+1+cnt,cmp);
62     kru();
63     return 0;
64 }

 

以上是对最近一段时间做的Kru类的生成树的一个比较粗略的整理,感觉对于并查集的运用还是比较多滴。

 

转载于:https://www.cnblogs.com/sagali/p/10505353.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值