POJ 1903 & ZOJ 2469 & UVA 1326 Jurassic Remains (部分枚举)

来源:互联网 发布:刘义军 知乎 编辑:程序博客网 时间:2024/05/01 05:43

题意:给定n个只有大写字母组成的字符串,选取尽可能多的字符串,使得这些字符串中每个字母的个数都是偶数。n<=24

思路:直接枚举每个字符串的选或不选,复杂度是O(2^n)。其实还有更简便的方法。

对于每个字母,其实具体出现了多少次并不重要,重要的是奇数次还是偶数次,我们用0对应奇数次,1对应偶数次。对于每个字符串,我们就可以计算出对应的二进制数,方法如下。如果A出现奇数次,那么二进制数第一个位置为1,偶数次为0;如果B出现奇数次,那么二进制数第二个位置为1,偶数次为0……以此类推,每个位置都有一个对应的0或1。这样就组成了一个二进制数。所以我们就可以将题意转化为找到尽量多的数字,使得他们的异或和为0。

直接枚举复杂度是O(2^n),但是我们不妨枚举前n/2个数字的选或不选,将所有可以得到的异或值存在一个STL的map中(键为异或和,值为得到这个异或和的选或不选的状态集合,对于同一个键,保留选取的数字最多的情况),然后枚举后n/2个数字的选或不选,计算出每个异或和,在map中查找是否有异或和等的键(因为两个相同的数字异或值为0),更新答案。

这样的复杂度只有O(2^[n/2] * logn)。

#include<cstdio>#include<map>#define MAXN 30using namespace std;int n,a[MAXN];char s[1005];map<int ,int > F;int bitcount(int x) {return x? bitcount(x/2)+(x&1):0;} //计算一个数二进制表示后所包含的1的个数int main(){while(~scanf("%d",&n)){for(int i=0;i<n;++i){scanf("%s",s);a[i]=0;for(int j=0;s[j];++j) a[i]^=(1<<(s[j]-'A'));//将字符串转化成数}F.clear();//每次记得清空映射int n1=n/2,n2=n-n1;for(int i=0;i < (1<< n1);++i) //枚举前n/2个数的每种状态{int x=0;for(int j=0;j<n1;++j) x^=((i>>j)&1)*a[j];//计算每种状态的异或和if(F.count(x) || bitcount(i)>bitcount(F[x])) F[x]=i;//F记录每个异或值所对应的字符串选取状态}int ans=0;//答案记录最终的选取状态for(int i=0;i < (1<< n2);++i)  //枚举后n/2个数的每种状态{int x=0;for(int j=0;j<n2;++j) x^=((i>>j)&1)*a[j+n1];//计算每种状态的异或和if(F.count(x) && bitcount(i)+bitcount(F[x])>bitcount(ans)) ans=(i<<n1)|F[x];//如果找到异或和与前n/2个数中的相同的异或和,那么更新答案}printf("%d\n",bitcount(ans));for(int i=0;i<n;++i) if((ans>>i)&1) printf("%d ",i+1);printf("\n");}return 0;}

原创粉丝点击