简要题意:给出一个边权为1或0的无向图,求一颗生成树,使得边权和为斐波那契数。
这里有一个重要的结论:一张边权为1或0的无向图,如果权值p在最大生成树权值与最小生成树权值之间,那么一定可以构造出一棵权值为p的生成树。
这样就好办了。先求出最大生成树与最小生成树的权值,然后判断这两者之间是否有一个斐波那契数就可以了。
详见代码。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cmath>
#include<stack>
#include<vector>
using namespace std;
const int N = 100001;
struct node {
int u, v;
bool w;
bool operator < (const node b) const {
return w < b.w;
}
}edge[N];
int fa[N];
long long fib[100];
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int main() {
// freopen("OI.out", "w", stdout);
//给斐波那契打表
fib[1] = fib[2] = 1;
for(int i = 3; i <= 100; i++)
fib[i] = fib[i - 1] + fib[i - 2];
int T;
cin >> T;
for(int ijk = 1; ijk <= T; ijk++) {
printf("Case #%d: ", ijk);
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
sort(edge + 1, edge + 1 + m);
int mx = 0, mn = 0;
int cnt = 0;
//构造最小生成树。
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= m; i++) {
if(cnt == n - 1) break;
int x = find(edge[i].u), y = find(edge[i].v);
if(x != y) {
cnt++;
mn += edge[i].w;
fa[x] = y;
}
}
if(cnt < n - 1) { //无法构成生成树,下同
printf("No\n");
continue;
}
//构造最大生成树
cnt = 0;
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = m; i >= 1; i--) {
if(cnt == n - 1) break;
int x = find(edge[i].u), y = find(edge[i].v);
if(x != y) {
cnt++;
mx += edge[i].w;
fa[x] = y;
}
}
if(cnt < n - 1) {
printf("No\n");
continue;
}
//两者之间是否有一个斐波那契
for(int i = 1; ; i++)
if(fib[i] >= mn && fib[i] <= mx) {
printf("Yes\n");
break;
} else if(fib[i] > mx){
printf("No\n");
break;
}
// printf("%d %d\n", mx, mn);
}
return 0;
}