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

国庆万岁!!!!!

【题目大意】

给定一张无向图,有两种边的类型为0和1。求一个最小生成树使得边0有k条。

【思路】

跑两次Kruskal。

第一次的时候优先选择边1,然后判断有哪些边0还不能连通,那么这些边0是必须要选取的。如果必须要选的边0大于k,那么直接输出无解。

第二次的时候先合并那么必须要选取的边0,然后在剩下的边0中左右还没有连通的里选取。如果把所有都选上了之后边0的数量还是没有到k,那么直接输出无解。

截下来按照普通Kruskal的方法把边1合并掉。

复制代码
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<vector>
  6 using namespace std;
  7 const int MAXN=20000+50;
  8 const int MAXM=100000+50;
  9 struct Edge
 10 {
 11     int u,v;
 12 }edge[MAXM];
 13 int n,m,k;
 14 int vis[MAXM];
 15 int L,R; 
 16 int fa[MAXN],h[MAXN]; 
 17 vector<int> mustchoose;
 18 
 19 void initset(){for (int i=1;i<=n;i++) fa[i]=i,h[i]=1;}
 20 
 21 int find(int x)
 22 {
 23     int r=x;
 24     while (fa[r]!=r) r=fa[r];
 25     while (fa[x]!=r)
 26     {
 27         int tmp=fa[x];
 28         fa[x]=r;
 29         x=fa[x];
 30     }
 31     return r;
 32 }
 33 
 34 void unionset(int a,int b)
 35 {
 36     if (h[a]>=h[b])
 37     {
 38         fa[b]=a;
 39         if (h[a]==h[b]) h[a]++;
 40     }
 41     else fa[a]=b;
 42 }
 43 
 44 void init()
 45 {
 46     scanf("%d%d%d",&n,&m,&k);
 47     L=0,R=m+1;
 48     for (int i=1;i<=m;i++) 
 49     {
 50         int u,v,c;
 51         scanf("%d%d%d",&u,&v,&c);
 52         if (c) edge[++L]=(Edge){u,v};
 53             else edge[--R]=(Edge){u,v};
 54     }
 55 }
 56 
 57 void solve()
 58 {
 59     int t=0; 
 60     initset();
 61     for (int i=1;i<=m;i++)
 62     {
 63         int fa=find(edge[i].u),fb=find(edge[i].v);
 64         if (fa!=fb)
 65         {
 66             unionset(fa,fb);
 67             if (i>=R)
 68             {
 69                 vis[i]=1;
 70                 t++;
 71                 mustchoose.push_back(i);
 72             }
 73         }
 74     }
 75     if (t>k)
 76     {
 77         puts("no solution");
 78         return;
 79     }
 80     // 找出必须要选择的鹅卵石路 
 81     
 82     initset();
 83     for (int i=0;i<mustchoose.size();i++)
 84     {
 85         int fa=find(edge[mustchoose[i]].u),fb=find(edge[mustchoose[i]].v);
 86         unionset(fa,fb);        
 87     }
 88     for (int i=R;i<=m;i++)
 89         if (t<k && !vis[i])
 90         {
 91             int fa=find(edge[i].u),fb=find(edge[i].v);
 92             if (fa!=fb)
 93             {
 94                 unionset(fa,fb);
 95                 vis[i]=1;
 96                 t++;
 97             }
 98         }
 99     if (t<k)
100     {
101         puts("no solution");
102         return;
103     }
104     //先选择必须要的鹅卵石路,然后再用其他鹅卵石路填充
105     
106     for (int i=1;i<=L;i++)
107     {
108         int fa=find(edge[i].u),fb=find(edge[i].v);
109         if (fa!=fb)
110         {
111             unionset(fa,fb);
112             vis[i]=1;
113         }
114     } 
115     for (int i=1;i<=L;i++) if (vis[i]) printf("%d %d %d\n",edge[i].u,edge[i].v,1);
116     for (int i=R;i<=m;i++) if (vis[i]) printf("%d %d %d\n",edge[i].u,edge[i].v,0);
117 }
118 
119 int main()
120 {
121     init();
122     solve();
123 } 
复制代码

 

posted @   iiyiyi  阅读(558)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
历史上的今天:
2015-10-01 【SPFA】POJ3259-Wormhole
点击右上角即可分享
微信分享提示