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

xiaoxiao2021-02-27  202

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; }
转载请注明原文地址: https://www.6miu.com/read-11197.html

最新回复(0)