Bzoj 3624: [Apio2008]免费道路 (贪心+生成树)

Posted Hzoi_joker

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bzoj 3624: [Apio2008]免费道路 (贪心+生成树)相关的知识,希望对你有一定的参考价值。

Sample Input

5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1

Sample Output

3 2 0
4 3 0
5 3 1
1 2 1
  这道题当时光读题就读半天,现在大概翻译一下:
    我们需要对于该图建一棵生成树使所有点连通,并且这棵树里有且只有K条白边。
   读明白后就想到了[国家集训队2012]tree(陈立杰),那道题也是类似,要求白边数量恰好为need,但是那道题要求是最小生成树,而这道题只要是生成树就好了。而且还得判断和不合法。然后就开始想,假设我们先扣掉所有白边,那么剩下的就是由黑边组成的一个个联通块了。然后呢?就不知道了,一开始想去用并查集搞,但是没搞出什么名堂,也就放弃了。
  正解的确还是最小生成树,额,或许不应该说是最小生成树,但是的确要用克鲁斯卡尔,我们先把黑边优先,这样,我们就可以先找出造出一个生成树的下限,如果k比他还小那么显然不行。同理,我们再白边优先,找出生成树的上限,如果k比他大那么仍然不行。
  合法性我们解决完了,答案怎么出来呢?
  让我们先回顾树的一个性质:当我们在一棵树上,从一个点向另一个点连边时树就会被破坏,但是,当我们拆开由这两个边组成的环上的任意一点时,树又变得合法。
  首先,对于我们找出白边下限时找出的白边我们都是无法找出黑边将他们替换的,说白了,我们必须选上他们。
  其次,对于剩下的白边,我们可以意识到,我们之所以没有把它们在找下限时把它找到是因为它可以被一个黑边或者白边代替,如果它被白边代替,那么我们仍然不必选他,因为那条白边是一定要选的,当然,我们可以把它和那条白边替换,但既然是spj,这有什么意义呢?
   如果他是被黑边替换,那么我们如果先选他也就不必再去选那条黑边,因此,我们先把必须选白边选上,再贪心去找那些被黑边替换的白边,找够了就只去找黑边,最终输出答案就好了。
  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <queue>
  6 #include <algorithm>
  7 #include <cmath>
  8 #include <map>
  9 #define N 20005
 10 #define M 100005
 11 using namespace std;
 12 int n,m,t,fa[N];
 13 struct ro
 14 {
 15     int to,from,l;
 16     bool bj;
 17 }road[M];
 18 bool px1(ro a,ro b)
 19 {
 20     return a.l>b.l;
 21 }
 22 bool px2(ro a,ro b)
 23 {
 24     return a.l<b.l;
 25 }
 26 int find(int x)
 27 {
 28     if(fa[x]==x)return x;
 29     return fa[x]=find(fa[x]);
 30 }
 31 void hb(int x,int y)
 32 {
 33     int a=find(x),b=find(y);
 34     fa[a]=b;
 35 }
 36 int ans[N];
 37 int main()
 38 {
 39     scanf("%d%d%d",&n,&m,&t);
 40     int sum=0;
 41     for(int i=1;i<=n;i++)fa[i]=i;
 42     for(int i=1;i<=m;i++)
 43     {
 44         scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].l);
 45         if(!road[i].l)sum++;
 46     }
 47     sort(road+1,road+1+m,px1);
 48     int js1=0,js2=0;
 49     for(int i=1;i<=m;i++)
 50     {
 51         int x=road[i].from,y=road[i].to;
 52         if(find(x)!=find(y))
 53         {
 54             js1++;
 55             if(!road[i].l)
 56             {
 57                 road[i].bj=1;
 58                 js2++;
 59             }
 60             hb(x,y);
 61         }
 62         if(js1==n-1)break;
 63     }
 64     if(js1!=n-1||js2>t)
 65     {
 66         printf("no solution\\n");
 67         exit(0);
 68     }
 69     sort(road+1,road+1+m,px2);
 70     js1=0,js2=0;
 71     for(int i=1;i<=n;i++)fa[i]=i;
 72     for(int i=1;i<=sum;i++)
 73     {
 74         if(road[i].bj)
 75         {
 76             int x=road[i].from,y=road[i].to;
 77             hb(x,y);
 78             js2++;
 79             js1++;
 80             ans[js1]=i;
 81         }
 82     }
 83     for(int i=1;i<=m;i++)
 84     {
 85         int x=road[i].from,y=road[i].to;
 86         if(find(x)!=find(y))
 87         {
 88             if(!road[i].l)
 89             {
 90                 js2++;
 91             }
 92             js1++;
 93             ans[js1]=i;
 94             hb(x,y);
 95         }
 96         if(js2==t)i=sum,js2=-1;
 97     }
 98     if(js2!=-1)
 99     {
100         printf("no solution\\n");
101         exit(0);
102     }
103     for(int i=1;i<=n-1;i++)
104     {
105         printf("%d %d %d\\n",road[ans[i]].from,road[ans[i]].to,road[ans[i]].l);
106     }
107     return 0;
108 }
View Code

以上是关于Bzoj 3624: [Apio2008]免费道路 (贪心+生成树)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3624: [Apio2008]免费道路

bzoj3624Apio2008—免费道路

Bzoj 3624: [Apio2008]免费道路 (贪心+生成树)

BZOJ 3624: [Apio2008]免费道路 [生成树 并查集]

Kruskal+贪心思想BZOJ3624-[Apio2008]免费道路

BZOJ 3624 [Apio2008]免费道路:并查集 + 生成树 + 贪心恰有k条特殊路径