UVA1326 Jurassic Remains
知识点:集合的整数表示 + 折半枚举 +异或
一.题意
选择最多的串,使得这些串中出现的每一位大写字母都是出现了偶数次,并且输出串的个数,和按照升序说出选择了哪几个串
二.题解
把二进制上的每一位当作是一个大写字母(A->1,B->2,C->4……),然后通过异或来判断出现的次数是奇次还是偶次,并且通过折半枚举,将n分成两半来枚举
#include<stdio.h>
#include<string.h>
#include<set>
#include<map>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,a[30];
map <int,int> table;
int bitcount(int x){ //求x对应二进制中1的个数
return (x == 0 ? 0 : (x & 1) + bitcount(x>>1));
}
int main(){
while(cin>>n){
string s;
for(int i = 0; i < n; i++){
cin>>s;
a[i] = 0;
for(int j = 0; j < s.size(); j++)
//记录第i串中出现的每个字母的奇偶,奇数次为1,偶数次为0
a[i] ^= (1 << (s[j] - 'A'));
}
//将n分成两半
int left = n/2;
int right = n - left;
//多组数据,每次清空table表
table.clear();
for(int i = 0; i < (1<<left); i++){ //枚举含有全部n/2个元素的集合
int x = 0;
for(int j = 0; j < left; j++) if(i >> j & 1)//当i >> j为1时就代表选择了第j串
x ^= a[j];
//x没出现过或者更新为一个同样得到结果为x,但是选择了更多串的结果
if(!table.count(x) || bitcount(table[x]) < bitcount(i))
table[x] = i;
}
int res = 0;
for(int i = 0; i < (1<<right); i++){
int x = 0;
for(int j = 0; j < right; j++) if(i >> j & 1)
x ^= a[left + j];
//如果这个x出现过那么异或结果就为0,即出现过的每个大写字母都是偶数次
//并且每次更新为选择串更多的结果
if(table.count(x) && bitcount(res) < (bitcount(table[x]) + bitcount(i)))
//别忘记前面还有选择了的串,就把前面的串向左移动left位,在异或table[x]
res = (i<<left) ^ table[x];
}
printf("%d\n",bitcount(res));
int k = 1;
while(res){
if(res & 1)
printf("%d ",k);
k++;
res >>= 1;
}
puts("");
}
return 0;
}