研究了一天最小生成树 ,衍生出来包括最小瓶颈树,次小生成树,最小树形图之类的算法,前两者基本能搞定,最后那个就。。不太懂了日后再回去看吧似乎很少用到,抄个模板以备不时之需 先谈谈最小瓶颈树 首先注意这两个定理:
命题:无向图的最小生成树一定是瓶颈生成树。
命题:瓶颈生成树不一定是最小生成树
好了 知道这个之后无向图的瓶颈生成树就搞定了 贴上一道例题 :
Submit: 693 Solved: 463
[Submit][Status][Discuss]
2429: [HAOI2006]聪明的猴子
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 693 Solved: 463
[Submit][Status][Discuss]
Description
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。
现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。
在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。
【问题】 现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食。
Input
第1行为一个整数,表示猴子的个数M(2<=M<=500);
第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1--1000之间);
第3行为一个整数表示树的总棵数N(2<=N<=1000);
第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000--1000)。
(同一行的整数间用空格分开)
Output
包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数
Sample Input
4
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2
1 2 3 4
6
0 0
1 0
1 2
-1 -1
-2 0
2 2
Sample Output
3
赤裸裸的瓶颈生成树问题 模板题 贴个题解:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
struct ss
{
int x1,y1;
double ju;
}q[1010000];
int f[1001],v[1001],x[1001],y[1001];
int m,n,len;
inline int read()
{
char y; int x=0,f=1; y=getchar();
while (y<'0' || y>'9') {if (y=='-') f=-1; y=getchar();}
while (y>='0' && y<='9') {x=x*10+int(y)-48; y=getchar();}
return x*f;
}
double pow(int x1,int y1,int x2,int y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
bool cmp(ss xx,ss yy)
{
return xx.ju<yy.ju;
}
int find(int o)
{
if (f[o]!=o)f[o]=find(f[o]);
return f[o];
}
int main()
{
m=read(); len=0;
for (int i=1;i<=m;++i)
{
v[i]=read();
}
n=read();
for (int i=1;i<=n;++i)
{
x[i]=read(); y[i]=read(); f[i]=i;
}
for (int i=1;i<=n-1;++i)
for (int j=i+1;j<=n;++j)
if (i!=j)
{
++len;
q[len].x1=i; q[len].y1=j; q[len].ju=pow(x[i],y[i],x[j],y[j]);
}
double ans=-1;
sort(q+1,q+len+1,cmp);
for (int i=1;i<=len;++i)
{
int u1=find(q[i].x1); int u2=find(q[i].y1);
if (u1!=u2)
{
f[u1]=u2;
ans=max(ans,q[i].ju);
}
}
int sum=0;
for (int i=1;i<=m;++i)
if (v[i]>=ans)
++sum;
printf("%d",sum);
}
然后就是次小生成树 思路大概是:
先求出最小生成树,并记录下该最小生成树上的所有边!然后枚举该生成树的所有边删掉,如果仍能形成生成树,则max一下 最后得到的值就是次小生成树啦 代码如下:
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
using namespace std;
const int VN = 5e2+10;
const int EN = 2e5+10;
int V,E,f[VN],p[VN];
struct Edge{int x,y,w;}edge[EN];
bool cmp(Edge a,Edge b)
{
return a.w < b.w;
}
void SetFather()
{
for(int i = 0;i <= V;++i)
f[i] = i;
}
int Kruskal()
{
SetFather();
int ans = 0,vertex = 0;
sort(edge,edge+E,cmp);
for(int i = 0;i != E;++i)
{
int x = edge[i].x;
int y = edge[i].y;
int u,v;
for(u = x;u != f[u];u = f[u])
f[u] = f[f[u]];
for(v = y;v != f[v];v = f[v])
f[v] = f[f[v]];
if(u != v){
f[u] = v;
p[vertex++] = i;
ans += edge[i].w;
}
if(vertex == V-1)break;
}
return ans;
}
int SKruskal(int del)
{
SetFather();
int ans = 0,vertex = 0;
for(int i = 0;i != E;++i)
{
if(i == del)continue;
int x = edge[i].x;
int y = edge[i].y;
int u,v;
for(u = x;u != f[u];u = f[u])
f[u] = f[f[u]];
for(v = y;v != f[v];v = f[v])
f[v] = f[f[v]];
if(u != v){
f[u] = v;
vertex++;
//p[vertex++] = i;
ans += edge[i].w;
}
if(vertex == V-1)break;
}
if(vertex < V-1) ans = -1;
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&V,&E);
for(int i = 0;i != E;++i)
scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w);
int TotCost = Kruskal();
bool flag = 0;
int maxx=-111;
for(int i = 0;i != V-1;i++){
if(SKruskal(p[i]!=-1))
maxx=max(SKruskal(p[i]),maxx);
}
cout<<maxx<<endl;
}
return 0;
}
至于最后那个最小树形图就先不说了 那个算法是中国人发明的!!!!到如今遇到的也就Spfa和这个算法是中国人发明的!!!
贴个别人的模板吧 感谢分享:
int ZLEdmonds(int n,int map[maxn][maxn])
{
bool visited[maxn],flag[[maxn];
int pre[maxn];
int sum=0,i,j,k;
for( i=0; i<n; i++){
flag[i]=false;
map[i][i]=INF;
}
pre[0]=0;
while( true){
//求最短弧集合E0。
for( i=1; i<n; i++){
if( flag[i]) continue;
pre[i]=i;
for( j=0; j<n; j++){ //pre[i]保存终点为i的最短弧的起点。
if( !flag[j]&&map[j][i]<map[pre[i]][i])
pre[i]=j;
}
if( pre[i]==i) return -1;
}
//检查E0
for( i=1; i<n; i++){
if( flag[i]) continue;
for( j=0; j<n; j++)
visited[j]=false;
visited[0]=true;
j=i;
do{
visited[j]=true;
j=pre[j];
}while( !visited[j]);
if( !j) continue; //没有找到环。
i=j;//将整个环的权值保存,累计入原图的最小树形图
do{
sum+=map[pre[j]][j];
j=pre[j];
}while( j!=i);
j=i;//对于环上的点有关的边,修改边权
do{
for( k=0; k<n; k++){
if( !flag[k]&&map[k][j]&&map[k][j]<INF&&k!=pre[j])
map[k][j]-=map[pre[j]][j];
}
j=pre[j];
}while( j!=i);
//缩点,将整个环缩成i号点,所有环上的点有关的边转移到点i
for( j=0; j<n; j++){
if( j==i) continue;
for( k=pre[i]; k!=i; k++){
if( map[k][j]<map[i][j])
map[i][j]=map[k][j];
if( map[j][k]<map[j][i])
map[j][i]=map[j][k];
}
}
//标记环上其他的点为被缩掉 下次再找Ei时不参与
for( j=pre[i]; j!=i; j=pre[j])
falg[j]=true;
//当前环缩点结束,形成新的图G',跳出继续求G'的最小树形图 ,累计入sum。
}
if( i==n){
for( i=0; i<n; i++)
if( !flag[i])
sum+=map[pre[i]][i];
break;
}
}
return sum;
}