Fibonacci Tree HDU - 4786(最小生成树Kruskal)

Fibonacci Tree HDU - 4786

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

题意:

给你一个图有1边和0边两种边,问你能否构成一个生成树使得所有边权加和为斐波那契数

分析:

做法是先生成一个最小生成树,即1边最少的情况,得到权值mins,再生成一个最大生成树,即1边最多的情况,得到权值maxs,那么这之间的所有数都对应一个权值为这个数的生成树

为什么,我tm也没想明白为什么,网上很多博客只说了做法,我只能结合一下,说一下笼统的想法

证明及其不严谨,仅供理解

利用数学归纳法证明如果最小为mins,最大为maxs,那么一定可以通过mins+1,mins+1+1…推到maxs

当点的个数为2的时候
这里写图片描述
最大是0,最小是1,一定成立

假设当节点个数为n的时候,该结论成立

我们证明有n+1个点时成立
假设现在我们的生成树是权值最小时的数,即权值为mins

假设我们要换一条0边,将这条0边删去之后一定变成了两棵树设为T1,T2
这里写图片描述
1.如果v1,v2中间恰好有一条1边,这样结论成立,得到mins+1
这里写图片描述
2.如果v1,v2中间没有1边可以替换,但是我们知道存在一个最大值maxs,那么说明一定存在一个联通区域,可以替换0边是1边增加,我们就假设是T1,因为T1的节点数小于等于n,且上面假设了n时结论成立,即当前T1的权值一定是小于最大权值的,也就是一定能得到具有当前权值+1的权值的新树,所以整个树也就得到了权值+1的新树,由此结论成立

code:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
struct Node{
    int u,v,w;
}edge[maxn];

bool cmp1(Node a,Node b){
    return a.w > b.w;
}

bool cmp2(Node a,Node b){
    return a.w < b.w;
}

int pre[maxn],fib[maxn],n,m;
int init(){
    fib[1] = 1;
    fib[2] = 2;
    for(int i = 3; fib[i] < maxn; i++){
        fib[i] = fib[i-1] + fib[i-2];
        if(fib[i] >= maxn)
            return i;

    }
}

int Find(int x){
    if(x == pre[x])
        return x;
    else return pre[x] = Find(pre[x]);
}

int Kruskal(){
    for(int i = 0; i <= n; i++){
        pre[i] = i;
    }
    int num = 0,sum = 0;
    for(int i = 0; i < m; i++){
        int u = Find(edge[i].u);
        int v = Find(edge[i].v);
        if(u != v){
            pre[u] = v;
            num++;
            sum += edge[i].w;
        }
        if(num == n - 1) break;
    }
    if(num == n-1) return sum;
    else return 0;
}

int main(){
    int T,cas = 0;
    int cnt = init();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i = 0; i < m; i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            edge[i].u = u;
            edge[i].v = v;
            edge[i].w = w;
        }
        sort(edge,edge+m,cmp1);
        int maxs = Kruskal();
        sort(edge,edge+m,cmp2);
        int mins = Kruskal();
        bool flag = 0;
        for(int  i = mins; i <= maxs; i++){
            if(flag) break;
            for(int k = 1; k < cnt; k++){
                if(i == fib[k]){
                    flag = 1;
                    break;
                }
            }
        }
        if(flag) printf("Case #%d: Yes\n",++cas);
        else printf("Case #%d: No\n",++cas);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/82454486
今日推荐