学习资料
概念及性质
瓶颈生成树 :无向图G的一颗瓶颈生成树是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。瓶颈生成树的值为T中最大权值边的权。——摘自百度百科
无向图的最小生成树一定是瓶颈生成树,但瓶颈生成树不一定是最小生成树。
两种求法
方法1:
引自 最小瓶颈生成树 By 祎隋
原文地址:https://www.cnblogs.com/degage/p/9740739.html
这里主要介绍一种期望O(M)(线性)的求法,其实主要就是二分,具体思路如下:
类比找第k大值的方法,首先随机一个边权w。
然后将不超过这个边权的边加入,遍历这张图。
如果图连通,那么瓶颈不超过w,于是只需考虑边权不超过w的边。
否则将这些连通点缩起来,考虑边权大于w的边。
每次将问题的规模缩小至一半。
期望时间复杂度O(m)。
方法2:
利用无向图的最小生成树一定是瓶颈生成树,但瓶颈生成树不一定是最小生成树这一性质,用Kruskal求出(kruskal可以找到“瓶颈”,且容易证明)
例题
luogu2504 [HAOI2006]聪明的猴子
题目传送门:luogu2504
题目描述
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。
现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。
在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。
【问题】现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食。
输入格式
输入文件monkey.in包括:
第1行为一个整数,表示猴子的个数M(2<=M<=500);
第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1–1000之间);
第3行为一个整数表示树的总棵数N(2<=N<=1000);
第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000–1000)。
(同一行的整数间用空格分开)
输出格式
输出文件monkey.out包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。
输入输出样例
输入 #1 复制
4
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2
输出 #1 复制
3
说明/提示
【数据规模】
对于40%的数据,保证有2<=N <=100,1<=M<=100
对于全部的数据,保证有2<=N <= 1000,1<=M=500
感谢@charlie003 修正数据
代码:Kruskal
/****************************
User:Mandy.H.Y
Language:c++
Problem:luogu2504
Algorithm:Kruskal
****************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
const int maxm = 505;
int n,m,size,cnt;
int jump[maxm],father[maxn];
struct Edge{
int u,v,w;
}edge[1000005];
struct Node{
int x,y;
}node[maxn];
template<class T>inline void read(T &x){
x = 0;bool flag = 0;char ch = getchar();
while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){
if(x > 9) putch(x / 10);
putchar(x % 10 | 48);
}
template<class T>void put(const T x){
if(x < 0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("monkey.in","r",stdin);
}
void readdata(){
read(m);
for(int i = 1;i <= m; ++ i) read(jump[i]),jump[i] = jump[i] * jump[i];
read(n);
for(int i = 1;i <= n; ++ i) read(node[i].x),read(node[i].y);
sort(jump + 1,jump + m + 1);
}
int dis(int i,int j){
return (node[i].x - node[j].x) * (node[i].x - node[j].x) +
(node[i].y - node[j].y) * (node[i].y - node[j].y);
}
void eadd(int u,int v,int w){
edge[ ++ size].v = v;
edge[size].u = u;
edge[size].w = w;
}
bool cmp(const Edge &a,const Edge &b){
return a.w < b.w;
}
int find(int x){
return father[x] == x ? x :father[x] = find(father[x]);
}
void merge(int x,int y){
father[find(x)] = find(y);
}
void work(){
for(int i = 1;i <= n; ++ i){
father[i] = i;
for(int j = i + 1;j <= n; ++ j){
int d = dis(i,j);
if(d > jump[m]) continue;
eadd(i,j,d);
}
}
sort(edge + 1,edge + size + 1,cmp);
int ans = 0;
for(int i = 1;i <= size; ++ i){
int w = edge[i].w,u = edge[i].u,v = edge[i].v;
if(find(u) != find(v)){
merge(u,v);
ans = w;
++cnt;
}
if(cnt == n - 1) break;
}
if(cnt < n - 1){
puts("0");
return;
}
int id = m - (lower_bound(jump + 1,jump + m + 1,ans) - jump) + 1;
//%%%%%求出弹跳距离大于等于ans的猴子的个数
put(id);
}
int main(){
// file();
readdata();
work();
return 0;
}