題意:給定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 30
using 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;
}