F - Fibonacci Tree-hdu4786-krusal

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值