【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 } 
时间: 2024-08-18 21:11:22

【Kruskal+贪心思想】BZOJ3624-[Apio2008]免费道路的相关文章

bzoj3624: [Apio2008]免费道路

具体题目:https://vjudge.net/problem/HYSBZ-3624 Description 一个王国有N个城市和M条无向道路,这M条道路中有一些是鹅卵石路一些是水泥路.现在国王要选择尽可能少的路免费,并且使每两个城市都有一条免费路径.国王打算保留刚好K条鹅卵石路.请问国王是否可以办到所有这些要求,如果能则输出路径的两个城市和路的种类,否则"no solution". HINTN <= 20000M <= 100000 Analysis如果能成功肯定是最小生

[APIO2008]免费道路

# [APIO2008]免费道路 ### 第一反应 考虑朴素的克鲁斯卡尔算法加一个限制,先选鹅卵石路,且选到k个就停止 带来的问题: - ~~叶子节点特殊处理,都选上~~(但其实是连通性) - ~~而且你诡异的发现,tm,这个鹅卵石路可以突破最小生成树!!!~~(不仔细看题面的后果) ### 正解 考虑上文中的连通性,先用水泥路跑一遍$Kruskal$,然后不连通的且用到鹅卵石路的都要选上.剩下的既然水泥路可以,那么鹅卵石路也可以代替嘛,先选鹅卵石路,选到$k$个就停止 emm,那么什么时候是无

[Apio2008]免费道路[Kruscal]

3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1292  Solved: 518[Submit][Status][Discuss] Description Input Output 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

bzoj 3624: [Apio2008]免费道路 生成树的构造

3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 111  Solved: 49[Submit][Status] Description Input Output 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 还是看的网上的标

3624: [Apio2008]免费道路

3624: [Apio2008]免费道路 https://www.lydsy.com/JudgeOnline/problem.php?id=3624 题意: 一张无向图,每种边有两种类型0和1.求一个最小生成树使得有k条0边. 分析: 为了满足有k条0边的限制,先考虑0边哪些必选,如果所有1边都加入后,还有0边可以使得图不连通,那么这些0边必须选. 把必须选的加上然后再加到k,然后再加1边.中间判一下是否必选的大于k,0边是否大于等于k. 代码: 1 #include<bits/stdc++.h

【bzoj3624】Apio2008—免费道路

http://www.lydsy.com/JudgeOnline/problem.php?id=3624 (题目链接) 题意 给出一张无向图,其中有0类边和1类边.问能否构成正好有K条0类边的生成树,并输出方案. Solution 先将所有1类边加入生成树,然后再加入0类边,那么现在加入的0类边就是必须加入的0类边,将它们打上标记.然后再将并查集初始化,继续加0类边直到数量达到K,最后加1类边. 代码 // bzoj3624 #include<algorithm> #include<io

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

题意: 一张图0,1两种边,构造一个恰有k条0边的生成树 优先选择1边构造生成树,看看0边是否小于k 然后保留这些0边,补齐k条,再加1边一定能构成生成树 类似kruskal的证明 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=2e4+5, M=1e5+5; typedef long

【[APIO2008]免费道路】

\(kruskal\)好题 \(0\)边的数量在某些情况下是可以无限制的调控的,前提是所有必须存在的边都在生成树里了 所以应该分别求出有哪些边是必须在生成树里的,我们可以先从大到小排序,求出有哪些\(0\)边必须在生成树里,之后再从小到大排序,求出那些\(1\)边必须在生成树里 之后剩下的边就可以随便放了,调控\(0\)边的个数恰好为\(k\)即可 代码 #include<iostream> #include<algorithm> #include<cstring> #

APIO2008免费道路

题目大意 给定一张n个点m条边的图,图上有两种边,求保证有k条第一种边的情况下的最小生成树 传送门 题解 考虑最小生成树kruskal算法 先找到不含限制的最小生成树,然后就可以知道哪些第一种边是必选的 然后跑第二遍kruskal,先把第一种边加到k条,然后加入第二种边就好 代码 #include<bits/stdc++.h> #define inf 1000000000 #define ll long long using namespace std; int read() { int x=