https://vjudge.net/problem/UVALive-2965
题意:
给出若干个由大写字母组成的字符串,要求选出尽量多的字符串,使得每个大写字母出现的次数是偶数。
思路:
如果说我们把每个字母映射为不同的数字,那么每个字符串就可以用不同的数字来表示,即按照二进制位转化各个字符。
如ABCD既可以表示为1111,ABCE可以表示为11101。
那么问题就转化成了给出n个数字,那么选择尽量多的数字使得他们的异或为0(每个字符出现偶数次,则他们的异或肯定为0)。
一开始我直接用的2^N的状态压缩但是t了,参考了训练指南,之后复杂度降为1.414^(N) * log(N)。
先把前n/2个数可以得到的数字用一个map存起来,之后枚举后面n - n/2个数可以得到的结果,直接在map里面寻找位数尽量大的就可以了。
求一个数的二进制位有多少个1,这题十分巧妙的用了二分。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <vector> 4 #include <map> 5 using namespace std; 6 7 int a[30]; 8 9 vector<int> rec; 10 map<int,int> mmp; 11 12 int calbit(int x) 13 { 14 if (x == 0) return 0; 15 16 return calbit(x / 2) + (x & 1); 17 } 18 19 int main() 20 { 21 int n; 22 23 while (scanf("%d",&n) != EOF) 24 { 25 mmp.clear(); 26 rec.clear(); 27 28 for (int i = 0;i < n;i++) 29 { 30 int tmp = 0; 31 32 char b[30]; 33 34 scanf("%s",b); 35 36 for (int j = 0;j < strlen(b);j++) 37 { 38 tmp ^= (1 << (b[j] - 'A')); 39 } 40 41 a[i] = tmp; 42 } 43 44 int n1 = n / 2; 45 int n2 = n - n1; 46 47 for (int i = 0;i < (1 << n1);i++) 48 { 49 int tmp = 0; 50 51 for (int j = 0;j < n1;j++) 52 { 53 if (i & (1 << j)) tmp ^= a[j]; 54 } 55 56 if (mmp.find(tmp) != mmp.end() || calbit(mmp[tmp]) < calbit(i)) mmp[tmp] = i; 57 } 58 59 int ans = 0; 60 61 for (int i = 0;i < (1 << n2);i++) 62 { 63 int tmp = 0; 64 65 for (int j = 0;j < n2;j++) 66 { 67 if (i & (1 << j)) tmp ^= a[n1+j]; 68 } 69 70 if (mmp.find(tmp) != mmp.end()) 71 { 72 if (calbit(i) + calbit(mmp[tmp]) > calbit(ans)) ans = mmp[tmp] ^ (i << n1); 73 } 74 } 75 76 for (int i = 0;i < n;i++) 77 { 78 if (ans & (1 << i)) rec.push_back(i); 79 } 80 81 printf("%d ",rec.size()); 82 83 for (int i = 0;i < rec.size();i++) 84 { 85 printf("%d%c",rec[i] + 1,i == rec.size() ? ' ': ' '); 86 } 87 88 printf(" "); 89 } 90 91 return 0; 92 }