HDOJ 4786 - Fibonacci Tree

 

Fibonacci Tree

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4098    Accepted Submission(s): 1267


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 <= 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
 

Source

题目大意:一个图有若干条白边和若干条黑边,能否生成一个树,使得生成树的白边的条数为斐波那契数。斐波那契数的定义为1,2,3,5,8.……。


解题思路:以白边为最优建立一个树,得出此时白边的条数记为r。以黑边为最优建立一个树,得出此时白边的条数记为l。至此,得出了建成一棵树白边的范围,只要判断区间(l,r)内有没有斐波那契数即可。这种方法的意思就是,由白边最优树和黑边最优树可以构建出白边条数在区间(l,r)内的任意一棵树,构建的意思是在原树中删去一条边,然后再添加一条边。


证       明:只要明白白边最优树与黑边最优树的含义就可以了,黑边最优树就是无论怎样构建出来的树,新树的黑边数不可能超过黑边最优树的黑边数。通俗的说,就是黑边最优树是不可能加入一条白边,再删除一条黑边的。所以说只要可以加入一条白边,再删除一条黑边的树就不是黑边最优树,既然不是黑边最优树,就可以加一条白边,再删除一条黑边,就可以不断的减少白边数,反过来就可以有黑边最优树生成任意不是黑边最优树。


具体算法:黑边最优时,此时白边的权值就是1(题目中已输入,无需转化),黑边的权值为0,利用kruskal算法,就可一算出来了。 白边最优时,此时因为题目中输入的数据白边的权值为1,所以将白边的权值设为0,黑边的权值设为1(想想怎么设置比较快?),生成最小树,这时用 顶点数-1- 权值和算出来的就是白边数(想想为什么?)。在生成最小树时也可以用prim算法,不过kruskal简单些。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_V = 100005;
const int MAX_E = 100005;

int T, V, E, num;
int par[MAX_V];
int ran[MAX_V];

void init(int n) {
    for(int i = 0; i < n; ++i) {
        par[i] = i;
        ran[i] = 0;
    }
}

int fin(int x) {
    if(par[x] == x) {
        return x;
    } else {
        return par[x] = fin(par[x]);
    }
}

bool same(int x, int y) {
    return fin(x) == fin(y);
}

void unite(int x, int y) {
    x = fin(x);
    y = fin(y);
    if(x == y) return ;
    if(ran[x] < ran[y]) {
        par[x] = y;
    } else {
        par[y] = x;
        if(ran[x] == ran[y]) ran[x]++;
    }
}

struct edge {
    int u, v, cost;
};

bool comp(const edge & e1, const edge & e2) {
    return e1.cost < e2.cost;
}

edge es[MAX_E];


int kruskal() {
    sort(es, es + E, comp);
    init(V);
    int res = 0;
    for(int i = 0; i < E; ++i) {
        edge e = es[i];
        if(!same(e.u, e.v)) {
            unite(e.u, e.v);
            res += e.cost;
            num++;
        }
    }
    return res;
}

int main()
{
    int mi, ma;
    scanf("%d", &T);
    for(int t = 1; t <= T; ++t) {
        num = 0;
        scanf("%d%d", &V, &E);
        for(int i = 0; i < E; ++i) {
            scanf("%d%d%d", &es[i].u, &es[i].v, &es[i].cost);
            es[i].u--;
            es[i].v--;
        }
        mi = kruskal();
        if(num != V - 1)  {
            printf("Case #%d: No\n", t);
            continue;
        }

        for(int i = 0; i < E; ++i) {
            es[i].cost = (1 - es[i].cost);
        }
        ma = V - 1 - kruskal();
        //cout << "max***" << ma << "min***" << mi << endl;
        bool flag = false;
        int x1 = 1, x2 = 2, temp;
        while(x1 <= ma) {
            if(mi <= x1 && x1 <= ma) {
                flag = true;
                break;
            }
            temp = x1 + x2;
            x1 = x2;
            x2 = temp;
        }
        if(flag) {
            printf("Case #%d: Yes\n", t);
        } else {
            printf("Case #%d: No\n", t);
        }
        /*if(t != T) {
            printf("\n");
        }*/
    }
    return 0;
}


 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值