【最小生成树专题】HDU 4786 Fibonacci Tree

本文介绍了一道关于寻找Fibonacci生成树的问题。在给定无向图中,通过求最小和最大生成树来判断是否存在生成树的白色边数为Fibonacci数,并提供了两种AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://acm.hdu.edu.cn/showproblem.php?pid=4786

Fibonacci Tree

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6619    Accepted Submission(s): 2033

Problem Description

  Coach Pang is interested in Fibonacci numbers while Uncle Yang wants him to do some research on Spanning Tree. So Coach Pang decides to solve the following problem:
  Consider a bidirectional graph G with N vertices and M edges. All edges are painted into either white or black. Can we find a Spanning Tree with some positive Fibonacci number of white edges?
(Fibonacci number is defined as 1, 2, 3, 5, 8, ... )

Input

  The first line of the input contains an integer T, the number of test cases.
  For each test case, the first line contains two integers N(1 <= N <= 105) and M(0 <= M <= 105).
  Then M lines follow, each contains three integers u, v (1 <= u,v <= N, u<> v) and c (0 <= c <= 1), indicating an edge between u and v with a color c (1 for white and 0 for black).

Output

  For each test case, output a line “Case #x: s”. x is the case number and s is either “Yes” or “No” (without quotes) representing the answer to the problem.

Sample Input

2
4 4
1 2 1
2 3 1
3 4 1
1 4 0
5 6
1 2 1
1 3 1
1 4 1
1 5 1
3 5 1
4 2 1

Sample Output

Case #1: Yes
Case #2: No

Source

2013 Asia Chengdu Regional Contest

Recommend

We have carefully selected several similar problems for you:  6447 6446 6445 6444 6443 

题目大意:

给你一个无向图的N个点和M条双向边。告诉你每条边的权值。权值为1表示该边是白边,权值为0表示该边为黑边。

问:能否找到一棵生成树,使生成树白边的个数恰好为fibonacci数。如果能构成这样的Fibonacci树,输出Yes,否则输出No。

题目思路:因为边为白色的权值为1,黑色的权值为0。即在原图的基础上分别求一遍最小生成树和一遍最大生成树。

假设求出的最小生成树的总权值为 Min ,最大生成树的总权值为 Max , 那么我们可知生成树中最小的白边数为 Min ,最大白
边数为 Max。然后我们只要判断这两个值之间是否存在斐波那契数就行了。

AC code

遍历m条边

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int nmax=1e5+10;
const int mmax=2e5+10; 
int father[nmax];
int Fibo[nmax];
int n,m;//点数、边数
 
struct Edge{
	int u,v,val;//起点、终点、边权 
}edge[mmax];

bool cmp1(Edge a,Edge b){//按边权从小到大排序,求最小生成树 
	return a.val<b.val;
}

bool cmp2(Edge a,Edge b){//按边权从大到小排序,求最大生成树
	return a.val>b.val;
} 

int Fibonacci(){//返回Fibo[i]<nmax的最大下标i 
	Fibo[1]=1;
	Fibo[2]=2;
	for(int i=3;Fibo[i]<=nmax;i++){
		Fibo[i]=Fibo[i-1]+Fibo[i-2];
		if(Fibo[i]>=nmax){
			return i;
		}
	} 
}

int findFather(int u){
	if(u==father[u]) return u;
	else{
		int f=findFather(father[u]);
		father[u]=f;
		return f;
	}
}

void init(int n){
	for(int i=1;i<=n;i++){
		father[i]=i; 
	}
} 
int Kruskal(){//返回生成树的边权和 
	init(n);
	int cnt=0;//有效合并次数 
	int ans=0;//路径的边权和
	for(int i=0;i<m;i++){//边数从0~m
		int fu=findFather(edge[i].u);
		int fv=findFather(edge[i].v);
		if(fu!=fv){
			father[fu]=fv;
			ans+=edge[i].val;
			cnt++;
		} 
		if(cnt==n-1)
			break;
	}
	if(cnt==n-1)
		return ans;
	else
		return 0; 
}
int main(int argc, char** argv) {
	int t,kase=0;
	scanf("%d",&t);
	while(t--){
		memset(father,0,sizeof(father));
		memset(Fibo,0,sizeof(Fibo));
		memset(edge,0,sizeof(edge));
		kase++;
		scanf("%d %d",&n,&m);
		init(n);
		int u,v,w;
		for(int i=0;i<m;i++){
			//scanf("%d %d %d",&u,&v,&w);
			//edge[i].u=u;edge[i].v=v;edge[i].val=w;
			//edge[i].u=v;edge[i].v=u;edge[i].val=w;
			scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].val);
		}
		sort(edge,edge+m,cmp1);//按边权从小到大排序,求最小生成树
		int num1=Kruskal();
		sort(edge,edge+m,cmp2);//按边权从大到小排序,求最大生成树
		int num2=Kruskal();
		int FiboUp=Fibonacci();
		bool flag=0;
		//判断从边数下限到上限,是否存在Fibonacci数
		for(int i=num1;i<=num2;i++){
			if(flag==1)
				break;
			for(int j=1;j<FiboUp;j++){//这里是<,不是<= 
				if(Fibo[j]==i){
					flag=1;
					break;
				}
			} 
		}
		if(flag)
			printf("Case #%d: Yes\n",kase);
		else
			printf("Case #%d: No\n",kase);
	}
	return 0;
}

AC Code

遍历2*m条边,

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int nmax=1e5+10;
const int mmax=2e5+10; 
int father[nmax];
int Fibo[nmax];
int n,m;//点数、边数
 
struct Edge{
	int u,v,val;//起点、终点、边权 
}edge[mmax];

bool cmp1(Edge a,Edge b){//按边权从小到大排序,求最小生成树 
	return a.val<b.val;
}

bool cmp2(Edge a,Edge b){//按边权从大到小排序,求最大生成树
	return a.val>b.val;
} 

int Fibonacci(){//返回Fibo[i]<nmax的最大下标i 
	Fibo[1]=1;
	Fibo[2]=2;
	for(int i=3;Fibo[i]<=nmax;i++){
		Fibo[i]=Fibo[i-1]+Fibo[i-2];
		if(Fibo[i]>=nmax){
			return i;
		}
	} 
}

int findFather(int u){
	if(u==father[u]) return u;
	else{
		int f=findFather(father[u]);
		father[u]=f;
		return f;
	}
}

void init(int n){
	for(int i=1;i<=n;i++){
		father[i]=i; 
	}
} 
int Kruskal(){//返回生成树的边权和 
	init(n);
	int cnt=0;//有效合并次数 
	int ans=0;//路径的边权和
	for(int i=0;i<2*m;i++){//边数从0~2*m
		int fu=findFather(edge[i].u);
		int fv=findFather(edge[i].v);
		if(fu!=fv){
			father[fu]=fv;
			ans+=edge[i].val;
			cnt++;
		} 
		if(cnt==n-1)
			break;
	}
	if(cnt==n-1)
		return ans;
	else
		return 0; 
}
int main(int argc, char** argv) {
	int t,kase=0;
	scanf("%d",&t);
	while(t--){
		memset(father,0,sizeof(father));
		memset(Fibo,0,sizeof(Fibo));
		memset(edge,0,sizeof(edge));
		kase++;
		scanf("%d %d",&n,&m);
		init(n);
		int u,v,w;
		for(int i=0;i<m;i++){
			scanf("%d %d %d",&u,&v,&w);
			edge[i].u=u;edge[i].v=v;edge[i].val=w;
			edge[i].u=v;edge[i].v=u;edge[i].val=w;
			//scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].val);
		}
		sort(edge,edge+2*m,cmp1);//按边权从小到大排序,求最小生成树
		int num1=Kruskal();
		sort(edge,edge+2*m,cmp2);//按边权从大到小排序,求最大生成树
		int num2=Kruskal();
		int FiboUp=Fibonacci();
		bool flag=0;
		//判断从边数下限到上限,是否存在Fibonacci数
		for(int i=num1;i<=num2;i++){
			if(flag==1)
				break;
			for(int j=1;j<FiboUp;j++){//这里是<,不是<= 
				if(Fibo[j]==i){
					flag=1;
					break;
				}
			} 
		}
		if(flag)
			printf("Case #%d: Yes\n",kase);
		else
			printf("Case #%d: No\n",kase);
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值