Fibonacci Tree HDU - 4786 (并查集 生成树 思维题)

这是一道关于图论的问题,题目要求在给定的带颜色边的图中找到是否存在一种生成树方式,使得白色边的数量为斐波那契数。首先检查图是否连通,然后计算最大和最小可能的白色边数,最后判断这个范围内是否存在斐波那契数。
摘要由CSDN通过智能技术生成

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

大致题意:有n个点,m条边,告诉你每条边是黑色0还是白色1。问你将这些点生成一棵树后,所用连接的边是的白色的数量能否是斐波那契数。

思路:n个点的生成树需要n-1条边,我们首先判断该图是否连通,如果不连通那么直接输出no。接下来,我们需要求出构成树时所能用的最多的白色边数Max和最少白色边数Min。我们可以先用白色的边去构图,用并查集来判断连接的点的数量n’,边数即为n’-1,那么最多白色边数Max=n’-1,然后我们再用黑色的边去构图,同理求出最多的黑色边数n”,那么最少需要的白色边数Min=n-1-n”(因为黑边加白边总边数为n-1)。白色边的数量所能取到的范围即[Min,Max](我们可以这样想,因为最多取Max条白边,最少取Min条白边能构成树,所以每当我们减少一条白边时(不要动那些最少需要的白边)我们都能找到一条黑边使得图依旧连通),然后判断一下这个区间内是否有斐波那契数即可。

代码如下

#include<iostream>
#include<algorithm>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=100005;

int pre[maxn];

int vis[100005];
int n,m;

struct node
{
    int x,y,c;
 } edge[maxn];

int find(int x)
{
   int r=x;
   while (pre[r]!=r)
   r=pre[r];
   int i=x; int j;
   while(i!=r)
   {
       j=pre[i];
       pre[i]=r;
       i=j;
   }
   return r;
}


int solve(int k)
{
    int num=0;
    for(int i=1;i<=n;i++)
    pre[i]=i;

    for(int i=1;i<=m;i++)
    {
        if(edge[i].c!=k)//k=1时取黑边构图 ,k=0时取白边构图,k=2时两个都取
        {
            int f1=find(edge[i].x);
            int f2=find(edge[i].y);
            if(f1!=f2)
            {
                pre[f2]=f1;
                num++;//每新增一个点,边数加一
            }
        }
    }
    return num;
}

int main()
{
    int f[50];//存放斐波那契数
    memset(vis,0,sizeof(vis));
    vis[1]=vis[2]=f[1]=1;
    f[2]=2;
    for(int i=3;i<=24;i++)
    {
        f[i]=f[i-1]+f[i-2];
        vis[f[i]]=1;//将斐波那契数标记为1
    }

    int T;
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
    {
        int flag=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].c);


        printf("Case #%d: ",t);

        if(solve(2)!=n-1)//首先判断是否连通
        {
            printf("No\n");
            continue;
        }
        int Min=n-1-solve(1);//求最少白边
        int Max=solve(0);//求最多白边
        for(int i=Min;i<=Max;i++)
        {
            if(vis[i]==1)
            {
                printf("Yes\n");
                flag=1;
                break;
            }
        }
        if(!flag)
        printf("No\n");
    }
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值