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 没有注意是A,不是-‘a’
2. 排序后num和id就变了,所以只能struct。
#include<iostream>
#include<cstdio>
#include<set>
#include<vector>
#include<string.h>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 25
int bacc[N];
long long acc2[5000];
int cur[26],imax1,imax2;
typedef struct {
long long d;
int num;
int id;
}Node;
Node acc[5000];
bool mycmp(Node a,Node b)
{
return a.d<b.d;
}
int main() {
bool mycmp(Node a,Node b);
int n,k;
char s[1000];
int imax=0;
while(scanf("%d",&n)) {
imax=0;
imax1=0;
imax2=0;
memset(acc,0,sizeof(acc));
memset(bacc,0,sizeof(bacc));
for(int i=0; i<n; i++) {
scanf("%s",s);
memset(cur,0,sizeof(cur));
for(int j=0; j<strlen(s); j++) {
cur[s[j]-'A']=(cur[s[j]-'A']+1)%2;
}
for(int j=0; j<26; j++)
if(cur[j]) {
bacc[i]+=(long long)(1<<j);
}
}
int L=1<<(n/2);
int cnt=L,p,t,row,rowcount=0;
for(int i=0; i<L; i++) {
t=i;
row=0;
rowcount=0;
while(t) {
p=t%2;
if(p) {
acc[i].d=acc[i].d^bacc[row];
rowcount++;
}
row++;
t=t>>1;
}
//cout<<i<<"---------"<<rowcount<<" "<<acc[i].d<<endl;
acc[i].num=rowcount;
acc[i].id=i;
}
sort(acc,acc+cnt,mycmp);
for(int i=0;i<cnt;i++)
{
acc2[i]=acc[i].d;
}
int R=1<<(n-n/2);
for(int i=0; i<R; i++) {
t=i;
row=0+n/2;
rowcount=0;
long long basecount=0;
while(t) {
p=t%2;
if(p) {
basecount=basecount^bacc[row];
rowcount++;
}
row++;
t=t>>1;
}
int index=lower_bound(acc2,acc2+cnt,basecount)-acc2;
while(index<cnt&&acc2[index]==basecount) {
if(imax<(rowcount+acc[index].num)) {
imax=rowcount+acc[index].num;
imax1=acc[index].id;
imax2=i;
}
index++;
}
}
cout<<imax<<endl;
int index=0;
while(imax1) {
if(imax1%2) cout<<index+1<<" ";
imax1=imax1>>1;
index++;
};
index=0;
while(imax2) {
if(imax2%2) cout<<index+1+n/2<<" ";
imax2=imax2>>1;
index++;
};
cout<<endl;
}
return 0;
}
其他优秀的方法
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int maxn = 24;
int n,val[maxn];
char s[1000];
map<int,int> table,table1;
int main() {
while(~scanf("%d",&n)) {
for(int i = 0;i < n;i++) {
scanf("%s",s);
val[i] = 0;
for(int j = 0;s[j];j++) val[i] ^= (1 << (s[j] - 'A'));
}
table.clear();
table1.clear();
int n1 = n / 2,n2 = n - n1;
//中途相遇法,先处理前半部分
for(int i = 1;i <= (1 << n1);i++) {
int nowval = 0,nowc = 0;
for(int j = 0;j < n1;j++) {
if(((1 << j) & i) != 0) {
nowval ^= val[j];
nowc++;
}
}
if(table.count(nowval) == 0 || table[nowval] < nowc) {
table[nowval] = nowc;
table1[nowval] = i;
}
}
//后半部分
int ans = 0,ansval = 0,ansi = 0;
for(int i = 1;i <= (1 << n2);i++) {
int nowval = 0,nowc = 0;
for(int j = 0;j < n2;j++) {
if(((1 << j) & i) != 0) {
nowval ^= val[j + n1];
nowc++;
}
}
if(table.count(nowval) != 0 && table[nowval] + nowc > ans) {
ans = table[nowval] + nowc;
ansval = nowval;
ansi = i;
}
}
//输出结果
printf("%d\n",ans);
for(int i = 0;i < n1;i++) if((1 << i) & table1[ansval]) printf("%d ",i + 1);
for(int i = 0;i < n2;i++) if((1 << i) & ansi) printf("%d ",i + n1 + 1);
printf("\n");
}
return 0;
}
#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
const int maxn=100;
int a[maxn]; /*a[i]的值的二进制表示 等于第i个字符串的
的“二进制”(从最低位到最高位依此表示A~Z
该字母有奇数个则该位为1,否则为0*/
char s[1005]; //临时存放字符串
map<int,int> table;
/*table的第二位表示怎么选字符串,如为7,二进制表示为111,
则选第1、2、3号字符串;table的第一位表示该种选法对应的
异或值*/
//x的二进制表示中‘1’的个数
int bitcount(int x)
{
return x==0?0:(x&1)+bitcount(x>>1);
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
{
scanf("%s",s);
a[i]=0;
for(int j=0;s[j]!='\0';j++)
a[i]^=1<<(s[j]-'A');
}
table.clear();
int n1=n/2,n2=n-n1;
for(int i=0;i<(1<<n1);i++) //2^n1种选取方法,二进制位上的值为1表示选取该二进制位对应的字符串
{
int x=0;
for(int j=0;j<n1;j++)if(i & (1<<j))
x^=a[j];
if(!table.count(x) || bitcount(table[x])<bitcount(i))
table[x]=i;
}
int ans=0;
for(int i=0;i<(1<<n2);i++)
{
int x=0;
for(int j=0;j<n2;j++)if(i & (1<<j))
x^=a[j+n1];
if(table.count(x)&&bitcount(table[x])+bitcount(i)>bitcount(ans))
ans=(i<<n1)^table[x];
}
printf("%d\n",bitcount(ans));
int k=0;
while(ans)
{
k++;
if(ans&1) printf("%d ",k);
ans>>=1;
}
printf("\n");
/* for(int i=0;i<n;i++)
if(ans & (1<<i))
printf("%d ",i+1);
printf("\n");*/
}
return 0;
}
---------------------
作者:EnjoyingAC
来源:CSDN
原文:https://blog.csdn.net/qq_37685156/article/details/79608707
版权声明:本文为博主原创文章,转载请附上博文链接!