文章标题 HDU 4786 : Fibonacci Tree(最小生成树--kruskal+并查集)

探讨如何通过两次最小生成树算法确定图中是否存在一棵生成树,该生成树中的白色边数量为斐波那契数。具体实现包括定义结构体、排序、查找等关键步骤。

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

Fibonacci Tree

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 <= 10 5) and M(0 <= M <= 10 5).
  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
题意:有一个图,有n个点,这m个点有m条边连着,每条边不是白色的就是黑色的,问能否找到一颗生成树,这棵树中白色的边刚好是斐波那契数。
分析:其实就是一个最小生成树的题,做两次最小生成树,第一次按白边为优先级,得到白边的最大值,第二次黑边为优先级,得到白边的最小值,最后只要判断最小值和最大值之间有没有斐波那契数就行了。
代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<math.h>
#include<map>
#include<queue> 
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m;
struct node {//定义结构体,表示连接的边和边的颜色,1为白色,0为黑色
    int x,y,col;
};
node a[100005];
int fa[100005];
int fb[1000];
int flag[100005];
void init(){//初始化
    memset(flag,0,sizeof (flag));
    for (int i=1;i<=n;i++){
        fa[i]=i;
    }
}
bool cmp1(node a,node b){//黑色为优先级
    return a.col<b.col;
}
bool cmp2(node a,node b){//白色为优先级
    return a.col>b.col;
}
int Find(int x){
    return fa[x]==x?x:fa[x]=Find(fa[x]);
}
int main ()
{
    int t;
    fb[0]=1;
    fb[1]=2;
    int count;
    for (int i=2;;i++){
        fb[i]=fb[i-1]+fb[i-2];
        if (fb[i]>100000){
            count=i;
            break;
        }
    }
    scanf ("%d",&t);
    int cnt=1;
    while (t--){
        scanf ("%d%d",&n,&m);
        init();//先初始化
        for (int i=0;i<m;i++){
            scanf ("%d%d%d",&a[i].x,&a[i].y,&a[i].col);
        } 
        if (n-1>m){//如果边数小于n-1,直接输出no
            printf ("Case #%d: No\n",cnt++);
            continue;
        }
        sort(a,a+m,cmp1);//按黑色优先级排序,得到白色边的最小值
        int sum=1;
        int white1=0;
        for (int i=0;i<m;i++){
            int fu=Find(a[i].x);
            int fv=Find(a[i].y);
            if (fu!=fv){
                sum++;
                if (a[i].col){
                    white1++;
                }
                if (!flag[a[i].x]){
                    flag[a[i].x]=1;
                }
                if (!flag[a[i].y]){
                    flag[a[i].y]=1;
                }
                fa[fu]=fv;
            }
            if (sum==n)break; 
        } 
        int tree_num=0;
        int judge=0;
        for (int i=1;i<=n;i++){//判断能否生成一棵树
            if (fa[i]==i) tree_num++;
            if (tree_num>1){
                judge=1;
                break;
            }
        }
        if (judge){//如果不行直接输出no
            printf ("Case #%d: No\n",cnt++);
            continue;
        }
        init();//求白边的最大值,初始化
        int white2=0;
        sum=1;
        sort(a,a+m,cmp2);
        for (int i=0;i<m;i++){
            int fu=Find(a[i].x);
            int fv=Find(a[i].y);
            if (fu!=fv){
                sum++;
                if (a[i].col){
                    white2++;
                }
                fa[fu]=fv;
            }
            if (sum==n)break; 
        }
        tree_num=0;
        for (int i=1;i<=n;i++){
            if (fa[i]==i) tree_num++;
            if (tree_num>1){
                judge=1;
                break;
            }
        }   
        if (judge){//判断是否是一棵树
            printf ("Case #%d: No\n",cnt++);
            continue;
        }
        //判断是含有斐波那契数,用二分法
        int place=lower_bound(fb,fb+count,white1)-fb;
        if (fb[place]>=white1&&fb[place]<=white2){
            printf ("Case #%d: Yes\n",cnt++);
        }
        else printf ("Case #%d: No\n",cnt++);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值