思路:
用一个数的每一位表示在这个状态下字符的状态,1表示有奇数个,0表示有偶数个。
把所有字符串分成两半。
在前一半枚举所有可能取的情况,把异或值和长度存入map<int,STT> stt。
可以知道,如果前一半取了某些串使得奇数次的字符组成了一个集合ODD,那么后一半一定也取出ODD这种情况才能使异或值为0。
所以仍然在后一半枚举所有可能取的情况,假设当前计算出来的数为x,如果stt中存在x,就更新最长的长度。
注意:不能把代表每一个字符串取或不取的数和每个字符有或没有的数混淆。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 24
#define maxs 5000
struct STT{
int x,len;
STT(int xx=0,int ll=0){
x=xx,len=ll;
}
};
int n;
int a[maxn+5];
map<int,STT> stt;
void init() {
stt.clear();
}
void print(int ans,int h) {
printf("%d\n",ans);
if(ans) {
int j=0;
while(h){
if(h&1) printf("%d ",j+1);
h>>=1;
j++;
}
printf("\n");
}
printf("\n");
}
void readin() {
char s[1000];
for(int i=0; i<n; i++) {
scanf("%s",s);
int len=strlen(s);
int x=0;
for(int j=0; j<len; j++) {
int y=s[j]-'A';
x^=(1<<y);
}
a[i]=x;
}
}
void make_f() { //前半段
for(int i=0; i<(1<<(n/2)); i++) {
int x=i,y=0;
int s=0;
while(x) {
if(x&1) {
s^=a[y];
}
x>>=1;
y++;
}
int j=0;
y=i;
while(y){
j+=(y&1);
y>>=1;
}
if(!stt.count(s)||stt[s].len<j) {
stt[s]=STT(i,j);
}
}
}
void make_b() { //后半段
int ans=0,h;
for(int k=0; k<(1<<(n-n/2)); k++) { //注意不能写成 k<(1<<(n/2))
int i=k<<(n/2);
int x=i,y=0;
int s=0;
while(x) {
if(x&1) {
s^=a[y];
}
x>>=1;
y++;
}
int j=0;
y=i;
while(y){
j+=(y&1);
y>>=1;
}
if(stt.count(s)&&stt[s].len+j>ans) { //如果stt[i].len==0则说明在前半段中并未出现这样的值
ans=stt[s].len+j;
h=i+stt[s].x;
}
}
print(ans,h);
}
int main() {
while(~scanf("%d",&n)) {
init();
readin();
make_f();
make_b();
}
return 0;
}
本文介绍了一种解决JurassicRemains问题的算法思路,通过将字符串分为两半,并利用位运算和哈希表来高效地寻找能够使字符串异或值为0的组合。重点介绍了如何枚举字符串子集并存储状态,最终找到最优解的过程。
862

被折叠的 条评论
为什么被折叠?



