http://acm.hdu.edu.cn/showproblem.php?pid=4786
坑死了。
开始没有看明白意思。以为是在白边建的图上做一个生成树。
直接用白边的数量判断是否为 n-1
后来发现是
给定黑白边,问生成树的价值是否是 斐波那契数,
先求最小生成树,再求最大生成树,。
卡一下就行了。
题目中用并查集也可以。
这个是用 krusal写的。。感觉模板能力还是需要加强啊。。
#include <bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,m;
int pa[100005];
int fb[26];
struct node
{
int from;
int to;
int w;
} edge[maxn*2];
int tol;
void add(int u0,int v0,int w0)
{
edge[tol].from=u0;
edge[tol].to=v0;
edge[tol++].w=w0;
}
bool cmp1(node a,node b)
{ return a.w<b.w;
}
bool cmp2(node a,node b)
{
return a.w>b.w;
}
void make_set()
{
for(int x=1;x<=m;x++)
pa[x]=x;
}
int find(int x)
{
if(x!=pa[x])
return pa[x]=find(pa[x]);
return pa[x];
}
int kruskal1(int n)
{
int ans=0,cnt=1;
make_set();
sort(edge+1,edge+tol+1,cmp1);
for(int i=1; i<=tol; i++)
{
int x=find(edge[i].from);
int y=find(edge[i].to);
if(x!=y)
{
pa[y]=x;
ans+=edge[i].w;
cnt++;
}
if(cnt==n)
break;
}
if(cnt<n)
return -1;
else return ans;
}
int kruskal2(int n)
{
int ans=0,cnt=1 ;
make_set();
sort(edge+1,edge+tol+1,cmp2);
for(int i=1; i<=tol; i++)
{
int x=find(edge[i].from);
int y=find(edge[i].to);
if(x!=y)
{
pa[y]=x;
ans+=edge[i].w;
cnt++;
}
if(cnt==n)//这块要注意,如果不是连通图,则直接可以跳出
break;
}
if(cnt<n) return -1;
else return ans;
}
int main()
{ std::ios::sync_with_stdio(false);
cin.tie(0);
int t;
int a,b,c;
int sum;
fb[1]=1;
fb[2]=2;
for(int i=3;i<=24;i++)
{ fb[i]=fb[i-1]+fb[i-2];
}
cin>>t;
for(int f=1;f<=t;f++)
{ memset(edge,0,sizeof(edge));
memset(pa,0,sizeof(pa));
sum=0;
tol=1;
//memset(s,0,sizeof(s));
cin>>m>>n;
for(int i=1;i<=n;i++)
{ cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
bool flag=false;
int sum1=kruskal1(m);
memset(pa,0,sizeof(pa));
int sum2=kruskal2(m);
if(sum1==-1||sum2==-1)
{ cout<<"Case #"<<f<<": No\n";
continue;
}
for(int i=1;i<=24;i++)
{ if(fb[i]>=sum1&&fb[i]<=sum2)
{flag=true;
break;
}
}
if(flag)
cout<<"Case #"<<f<<": Yes\n";
else
cout<<"Case #"<<f<<": No\n";
}
return 0;
}
又发现了一个用并查集的方法,简直不要太好。
通过并查集的合并次数就能判断 生成树是否成立。感觉比prim强太多。
计算两种情况
1 是只用0边,优先使用0边,剩下搞不到的用1边,这样是最小生成树。
2 如果都用一边,那么是最大的。
我感觉 和上面的一样啊。。
这个也是 krusal。。
注意条件,判断一下是否为 m-1
#include <bits/stdc++.h>
/*这道题也可以用并查集写,
上一个思路是用 kruskal算法,求一个最小生成树。
再求一个最大的生成树。
而用prim明显不如kruskal,kruskal求最大生成时也是可以的。
我打算先用并查集搞一下,在用优化过的prim搞一搞
具体思路是krusal联想出来的
如果存在生成树,那么合并次数一定是n-1,每次合并相当于建立一个边。(因为并查集是点的集合。)
先用0的边进行合并,(用0不一定能搞成生成树,所以减一下算1的边,这是最小的
然后在纯纯的以1的边建立,也不一定能搞成,加0才可以,但是可以保证是最大的,
然后看是否存在斐波那契在里面。
有一个道理要想明白就是 生成树的权是线性增长的,
*/
int fb[25];
const int maxn=100008;
using namespace std;
int pf[maxn];
struct Tree
{ int a,b,c;
}tree[maxn*2];
int m,n;
int find1(int s)
{ if(s==pf[s])
return s;
return pf[s]=find1(pf[s]);
}
int qs(int value)
{ for(int i=1;i<=m;i++)
pf[i]=i;
int x,y;
int sum=0;
for(int i=1;i<=n;i++)
{ x=find1(tree[i].a);
y=find1(tree[i].b);
if(x!=y&&tree[i].c!=value)
{ pf[x]=y;
sum++;
}
}
return sum;
}
int main()
{ std::ios::sync_with_stdio(false);
cin.tie(0);
int t;
int a,b,c;
fb[1]=1;
fb[2]=2;
for(int i=3;i<=24;i++)
fb[i]=fb[i-1]+fb[i-2];
cin>>t;
int tim=1;
while(t--)
{ cin>>m>>n;//n个边,m个点;
for(int i=1;i<=n;i++)
{ cin>>a>>b>>c;
tree[i].a=a;
tree[i].b=b;
tree[i].c=c;
}
if(qs(2)!=m-1)
{ cout<<"Case #"<<tim<<": No\n";
tim++;
continue;
}
//然后在合并一下
int sma=m-1-qs(1);
int big=qs(0);
//cout<<sma<<endl;
// cout<<big<<endl;
cout<<"Case #"<<tim;
tim++;
bool falg=false;
for(int i=1;i<=24;i++)
{ if(sma<=fb[i]&&big>fb[i])
{falg=true;break;}
}
if(falg)
cout<<": Yes\n";
else
cout<<": No\n";
}
return 0;
}