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

题目描述

传送门

题解

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

代码

#include
#include
#include
#include
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.pdinline 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]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);
}

总结

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

你可能感兴趣的:(题解,并查集,贪心)