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

57 篇文章 0 订阅
41 篇文章 0 订阅

题目描述

传送门

题解

贪心的思想:首先这一定是一棵生成树,那么我们先做生成树,尽量加水泥路,那么做完之后已经加入的鹅卵石路一定是必须要加的。那么我们打破第一次的生成树,再做一次生成树,首先把必须加的鹅卵石路都加进去,如果鹅卵石路不够k条的话再加进一些凑够k条,之后加水泥路使之成为一颗生成树。
也可以值么理解:如果把所有的水泥路都加入到生成树中的话会形成很多连通块,如果想让它成为强连通图的话各个连通块之间必须用水泥路来连接,那么这些水泥路就成了必须加的水泥路。注意生成树的性质:在生成树中任加一条原图边会形成一个环,在这个环中任拆一条边又会变成一个合法的生成树。那么就相当于在原生成树中拆掉了一些水泥路又加上了一些鹅卵石路使之恰好为k条。
那么无解的情况也会判断了吧?

代码

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

const int max_n=2e4+5;
const int max_m=1e5+5;

int n,m,k,sum[2],f[max_n];
struct hp{int u,v,ty;bool pd;}a[max_m];

inline int cmp(hp a,hp b){return a.ty>b.ty;}
inline int cmp1(hp a,hp b){return a.pd<b.pd||a.pd==b.pd&&a.ty<b.ty;}
inline int find(int x){
    if (x==f[x]) return x;
    f[x]=find(f[x]); return f[x];
}
inline void merge(int x,int y){
    int f1=find(x),f2=find(y);
    f[f1]=f2;
}

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=m;++i)
      scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].ty);
    sort(a+1,a+m+1,cmp);

    for (int i=1;i<=n;++i) f[i]=i;
    for (int i=1;i<=m;++i)
      if (find(a[i].u)!=find(a[i].v)){
        merge(a[i].u,a[i].v),a[i].pd=true,sum[a[i].ty]++;
        if (sum[0]+sum[1]==n-1) break;
      }

    if (sum[0]+sum[1]!=n-1||sum[0]>k){printf("no solution\n");return 0;}
    if (sum[0]==k){
        for (int i=1;i<=m;++i)
          if (a[i].pd) printf("%d %d %d\n",a[i].u,a[i].v,a[i].ty);
        return 0;
    }

    sum[1]=0;
    for (int i=1;i<=n;++i) f[i]=i;
    for (int i=1;i<=m;++i)
      if (a[i].pd&&a[i].ty==0) merge(a[i].u,a[i].v);
      else a[i].pd=false;
    sort(a+1,a+m+1,cmp1);

    for (int i=1;i<=m;++i)
      if (find(a[i].u)!=find(a[i].v)&&!a[i].pd&&!a[i].ty){
        merge(a[i].u,a[i].v); a[i].pd=true;
        sum[0]++;
        if (sum[0]==k) break;
      }
    if (sum[0]<k){printf("no solution\n");return 0;}
    for (int i=1;i<=m;++i)
      if (find(a[i].u)!=find(a[i].v)&&!a[i].pd&&a[i].ty){
        merge(a[i].u,a[i].v); a[i].pd=true;
        sum[1]++;
        if (sum[1]+sum[0]==n-1) break;
      }
    if (sum[0]+sum[1]!=n-1){printf("no solution\n");return 0;}

    for (int i=1;i<=m;++i)
      if (a[i].pd)
        printf("%d %d %d\n",a[i].u,a[i].v,a[i].ty);
}

总结

贪心的思路要大胆想。
注意生成树的性质。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值