UVALive-2965 中间相遇

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 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/qiang_____0712/article/details/85219136