Clarke and MST HDU - 5627 (并查集+贪心)

Problem Description
Clarke is a patient with multiple personality disorder. One day he turned into a learner of graph theory.
He learned some algorithms of minimum spanning tree. Then he had a good idea, he wanted to find the maximum spanning tree with bit operation AND.
A spanning tree is composed by n−1 edges. Each two points of n points can reach each other. The size of a spanning tree is generated by bit operation AND with values of n−1 edges.
Now he wants to figure out the maximum spanning tree.

Input
The first line contains an integer T(1≤T≤5), the number of test cases.
For each test case, the first line contains two integers n,m(2≤n≤300000,1≤m≤300000), denoting the number of points and the number of edge respectively.
Then m lines followed, each line contains three integers x,y,w(1≤x,y≤n,0≤w≤109), denoting an edge between x,y with value w.
The number of test case with n,m>100000 will not exceed 1.

Output
For each test case, print a line contained an integer represented the answer. If there is no any spanning tree, print 0.

Sample Input
1
4 5
1 2 5
1 3 3
1 4 2
2 3 1
3 4 7

Sample Output
1

大致题意:给你n个点m条边以及m条边的权值,让你构造一棵生成树,使得生成树的权值and和最大。

思路:(借用袁神的思路)首先我们得分析位运算与的特征,很明显,与运算和运算顺序没有关系,不像异或(那样这题就没得做了),那么要求一个生成树的与结果最大,就是所有边权在二进制相应位下都为1,那么就按位来一个枚举,从最大位开始,依次枚举2的倍数tmp,只有和tmp的与运算等于tmp的边权才是合法边,再验证是否能在合法边中找到生成树,如果能,就将这个tmp记录下来成为now,之后的tmp都要或(位运算)上这个now,最后枚举到1结束,输出当前now。
验证是否能够找到生成树就直接用并查集

代码如下

#include<iostream>
#include<algorithm>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn=300005;
struct node
{
    int x,y,w;
}edge[maxn];


int pre[maxn];

int n,m;

int find(int x)
{
   int r=x;
   while (pre[r]!=r)
   r=pre[r];
   int i=x; int j;
   while(i!=r)
   {
       j=pre[i];
       pre[i]=r;
       i=j;
   }
   return r;
}

//void join(int x,int y)
//{
//    int f1=find(x);
//    int f2=find(y);
//    if(f1!=f2)
//    {
//        pre[f2]=f1;
//    }
//}


int main()
{
    int T,temp;
    scanf("%d",&T);
    while(T--)
    {
           scanf("%d%d",&n,&m);

           for(int i=1;i<=m;i++)
           scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w);

           int now=0;
           for(int k=30;k>=0;k--)
           {
              temp=now|(1<<k);//假设第k位置上能为1(即所选的边第k位置上都为1,且能构成树),那么所能得到的最大值可能为now|(1<<k) 
                              //接下来验证,如果可以,则将temp赋给now 
              for(int i=1;i<=n;i++)
              pre[i]=i;

              for(int i=1;i<=m;i++)
              {
                 if((temp&edge[i].w)==temp)//表示该边可以选择,加入集合 
                 {
                    int f1=find(edge[i].x);
                    int f2=find(edge[i].y);
                    if(f1!=f2)
                    {
                        pre[f1]=f2;
                          }
                      }
                 }

              int sum=0;  //判断选择的边是否能构成树 
              for(int i=1;i<=n;i++)
              if(pre[i]==i)
              sum++;

              if(sum==1)//如果能 
              now=temp;  
           }
           printf("%d\n",now);  
    }
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值