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;
}