[BZOJ3624][Apio2008]免费道路(并查集)

本文介绍了一种构建特定数量鹅卵石路的生成树算法。首先确定必须包含的鹅卵石路,然后通过并查集确保生成树的连通性,并最终选择足够的鹅卵石路与水泥路构成一棵生成树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

=== ===

这里放传送门

=== ===

题解

显然题目中要求的是一棵包含k条鹅卵石路的生成树。首先把所有水泥道路都加入生成树里面去,看看有哪些鹅卵石路是不加进去就出不来生成树的。然后初始化并查集,先把那些必须加的鹅卵石路加进去,这样就保证了一定可以构造出生成树。然后剩下的就随便选就可以了,先选够k条鹅卵石路,再随便选几条水泥路搞出一棵树来就好啦。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,k,father[20010],d,root,cnt;
struct edge{
    int k,u,v;
    bool must,chs;
    bool operator < (const edge &x)const{
        return k>x.k;
    }
}e[100010];
int find(int x){
    if (father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;i++) father[i]=i;
    for (int i=1;i<=m;i++)
      scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].k);
    sort(e+1,e+m+1);
    for (int i=1;i<=m;i++){
        int r1,r2;
        if (e[i].k==0){d=i;break;}
        r1=find(e[i].u);r2=find(e[i].v);
        if (r1!=r2) father[r2]=r1;
    }
    for (int i=d;i<=m;i++){
        int r1,r2;
        r1=find(e[i].u);r2=find(e[i].v);
        if (r1!=r2){
            father[r2]=r1;
            e[i].must=true;
        }//判断哪些特殊边是必须加的
    }
    root=find(1);
    for (int i=2;i<=n;i++)
      if (find(i)!=root||m-d+1<k){
          printf("no solution\n");
          return 0;
      }
    for (int i=1;i<=n;i++) father[i]=i;
    for (int i=d;i<=m;i++)
      if (e[i].must==true){
          int r1,r2;
          r1=find(e[i].u);r2=find(e[i].v);
          if (r1!=r2){
              father[r2]=r1;++cnt;
              e[i].chs=true;//先把必须加的加进去,打使用标记
          }
      }
    if (cnt>k){
        printf("no solution\n");
        return 0;
    }
    for (int i=d;i<=m;i++){
        int r1,r2;
        if (cnt==k) break;
        if (e[i].must==false){
            int r1,r2;//补全k条特殊边
            r1=find(e[i].u);r2=find(e[i].v);
            if (r1!=r2){
                father[r2]=r1;++cnt;
                e[i].chs=true;
            }
        }
    }
    if (cnt<k){
        printf("no solution\n");
        return 0;
    }
    for (int i=1;i<d;i++){
        int r1,r2;//补全生成树
        if (cnt==n-1) break;
        r1=find(e[i].u);r2=find(e[i].v);
        if (r1!=r2){
            father[r2]=r1;++cnt;
            e[i].chs=true;
        }
    }
    for (int i=1;i<=m;i++)
      if (e[i].chs==true)
        printf("%d %d %d\n",e[i].u,e[i].v,e[i].k);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值