位运算——最长子集问题
【问题描述】有一个整数序列 a1,a2,…,an. 请找到一个集合{1,2,…,n}的一个最长子集S,使得对于∀i,j ∈ S, ai ⊕aj < min(ai,aj) ,其中 ⊕ 表示按位异或运算.
【输入形式】需要输入两行,第一行输入一个数 n (1 ≤ n ≤ 105), 表示数列a1,a2,…,an的长度。第二行输入 n 个整数: a1,a2,…,an (1 ≤ ai ≤ 109).
【输出形式】输出集合S的最大长度
【样例输入1】
3
1 2 3
【样例输出1】 2
【样例输入2】
5
1 1 1 1 1
【样例输出2】 5
【样例1说明】1 2 3 中2和3的异或结果等于1,既小于2又小于3
【提示】如果两个数的异或结果小于这两个数中的任何数,那么这两个数应该满足什么条件?
对于初学者来说,无论位运算还是本题都具有一定难度,本题改编自蓝桥杯的一个竞赛原题。在思考本题时,我们考虑到要寻找符合条件的子集时,无论如何都绕不开改子集的在最大数。
下面为同学写出的代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n,i;scanf("%d",&n);int a[32]={0};
for(i=0;i<n;i++)
{
int k;
scanf("%d",&k);
for(int j=31;j>=0;j--)
{
int m=k>>j;
if(m==1)
{
a[j]++;break;
}
}
}
int max=1;
for(i=0;i<n;i++)
{
if(max<a[i])
max=a[i];
}
printf("%d",max);
return 0;
}
以下为参考代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[32] = {0},n,i;
unsigned x;
scanf("%d",&n);
int max = 0;
for(i = 1;i <= n;i++)
{
int j;
scanf("%u",&x);
for(j = 30;j >= 0;j--)
if(x&(1<<j))
{
a[j]++;
if(max < a[j])max = a[j];
break;
}
}
printf("%d\n",max);
return 0;
}