LA2965侏罗纪
LA2965侏罗纪 http://122.207.68.93:9090/csuacmtrain/problem/viewProblem.action?id=3148 |
【题目描述】:给定n个大写字母组成的连续字符串,(n<=24),选取其中的m个组合,使相同的大写字母的出现次数之和为偶数。目标是让这个m最大。 |
【算法分析】: Ps:这道题看着比较简单,但是编码有难度。 【中途相遇法】: 首先,如果将每种状态用位表示,就是2^24种状态,1表示奇数个,0表示偶数个。利用异或运算的特殊性,模二加,奇数与奇数异或是偶数。所以我们的目的是找出m个数异或,使他们的结果为0。到这里为止,已经保证了编码的简便性。 但是,复杂度任然是难以支撑。朴素的算法为2^24(==16000000)种情况,超时。书中提供“中途相遇法”,将考虑的情况分为两半,一边是2^12(==4096)种,因为两个数异或,只有完全相等,才能为0。所以生成前半部分的“选取个数”(我们只关注这个)和“异或结果”的匹配的集合map。后半部分,每次生成一个新的匹配的时候,查找用nlog2(n)的复杂度即可。精髓在于“打表+查找”? 【综合复杂度】:2* 2^24+24*log2(24)约等于10000 |
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<stdlib.h> #include<math.h> #include<queue> #include<vector> #include<set> #include<map> #define MAXN 100+5 #define MAXM 100+5 #define oo 1e9 #define eps 0.001 #define PI acos(-1.0)//这个精确度高一些 #define REP1(i,n) for(int i=0;i<=(n);i++) #define REP2(i,n) for(int i=1;i<=(n);i++) #define DREP2(i,n) for(int i=(n);i>=1;i--) #define LL long long using namespace std;
int nums[MAXN];//表示统计的异或的结果 int n; int p1,p2; map<int,int> Map; //int getbits(int x) //{ // int ans=0; // while(x>0) // { // ans+=(1&x); // x>>1; // } // return ans; //}
int getbits(int x)//这个统计位数的函数值得学习 { return x==0?0:getbits(x/2)+(x&1); } void readin() { memset(nums,0,sizeof(nums)); char s[10005]; for(int i=0;i<n;i++) { cin>>s; int len=strlen(s); for(int j=0;j<len;j++) nums[i]=nums[i]^(1<<(s[j]-'A'));//存储的是z到a的结果 } return ; }
void makeMap() { Map.clear(); int p=n/2; for(int i=0;i<(1<<p);i++)//状态压缩,枚举每种组合情况 { // cout<<"i="<<i<<endl; int x=0;//存储每种组合异或的结果,注意初始化为0,0和任何数异或等于原数 for(int j=0;j<p;j++) {if (i&(1<<j)) x=x^nums[j];}//读取每位上的数字,注意nums从1开始存储 if (!Map.count(x))Map[x]=i;//查找关键字//注意i中既包含位数又包含选择信息 else if (getbits(Map[x])<getbits(i)) Map[x]=i; } return; } void getans() { int ans=0; int p=n-n/2;//枚举后p位,状态压缩比搜索好写啊
for(int i=0;i<(1<<p);i++) { int x=0; for(int j=0;j<p;j++) if (i&(1<<j)) x=x^nums[j+n/2];//同上 if (Map.count(x))//注意:我们的目标是异或结果为0,即两个x相等 { int k1=getbits(i),k2=getbits(Map[x]),m=getbits(ans); if (m<k1+k2) { // ans=(Map[x]<<(n/2))^i;//注意好移动步数 ans=(i<<(n/2))^Map[x]; }
} } cout<<getbits(ans)<<endl; if (ans>0) { int cnt=0,A[105]; for(int i=0;i<n;i++) if (ans &(1<<i)) A[cnt++]=i+1; for(int i=0;i<cnt-1;i++) cout<<A[i]<<" "; cout<<A[cnt-1]<<endl; }
cout<<endl; return; } int main(){ while(cin>>n && n) { readin(); makeMap();//构建前1--n/2组合异或的MAP,用状态压缩实现 getans(); } return 0; } |
【关键词】:位运算,中途相遇法,建模 |