hdu 4786 Fibonacci Tree 思路

探讨如何利用斐波那契数与生成树理论解决特定图论问题,通过Kruskal算法验证图的连通性,并确定生成树中白色边的数量是否符合斐波那契数列。

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条边,边有黑白两种颜色,问能否用这些边组成一个生成树,使其白色的边的个数为斐波拉切数(1,2,3,5)?

解题思路:

先用 kruskal跑一遍看图是否连通。然后只用白色边构图(用kruskal跑),看最多能用多少白色边(max),然后只用黑色边构图,看最少能用多少白色边(min)。最后枚举斐波拉切数,看是否有在这个范围内成立的斐波拉切数。

为什么找到了max图和min图就能保证一定存在这之间任意数量的白色边的图?

考察min图,加入一条未被用过的白色边,有两种情况

1:这个加入的边刚好覆盖在一条黑色边上,删除此黑色边即可得到min+1的图;

2:这个边没有覆盖任何边,那么只要能从联通这个边的两个节点的通路上删除一条黑色边,就可以min+1的图。假设不存在min+1的图,那么此通路上的所有边都是白色,那么直到把所有白色边都加入图中,任意白色边的两个端点间的通路就都没有黑色边。那么就会得到这样一种情况,最多只有一个节点既能连白色边,又能连黑色边,删除此节点,则得到两个不相连图,一个全是黑色边,一个全是白色边。对于这种情况,可以推出max=min,也就是说只要max>min,这个假设就是错的,所以min+1的图一定存在。

继续考察min+1的图,同上证明。。。可以证明白色边数量在min-max之间所有的图都存在。

好烦人的题目。。。

代码:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
using namespace std;
int f[maxn],febo[50];
int n,m;
struct edge
{
    int u,v,c;
}e[maxn];
int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}
int solve(int col)
{
    int num=0;
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++)
    {
        if(e[i].c!=col)
        {
            int x=find(e[i].u),y=find(e[i].v);
            if(x!=y)
            {
                f[x]=y;
                num++;
            }
        }
    }
    return num;
}
int main()
{
    febo[0]=1,febo[1]=2;
    int num;
    for(num=2;;num++)
    {
        febo[num]=febo[num-1]+febo[num-2];
        if(febo[num]>100000)
        break;
    }
    int ncase,T=0;
    scanf("%d",&ncase);
    while(ncase--)
    {
        printf("Case #%d: ",++T);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
        int tmp,mi,ma,tru=0;
        tmp=solve(2);
        if(tmp!=n-1)
        {
            printf("No\n");
            continue;
        }
        ma=solve(0);
        mi=n-1-solve(1);
        for(int i=0;i<num;i++)
        {
            if(febo[i]>=mi&&febo[i]<=ma)
            {
                tru=1;
                break;
            }
        }
        if(tru)
        printf("Yes\n");
        else
        printf("No\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值