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。后半部分,每次生成一个新的匹配的时候,查找用nlog2n)的复杂度即可。精髓在于“打表+查找”?

【综合复杂度】: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'));//存储的是za的结果

    }

    return ;

}

 

void makeMap()

{

    Map.clear();

    int p=n/2;

    for(int i=0;i<(1<<p);i++)//状态压缩,枚举每种组合情况

    {

//        cout<<"i="<<i<<endl;

        int x=0;//存储每种组合异或的结果,注意初始化为00和任何数异或等于原数

        for(int j=0;j<p;j++) {if (i&(1<<j)) x=x^nums[j];}//读取每位上的数字,注意nums1开始存储

        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;

}

【关键词】:位运算,中途相遇法,建模

posted @ 2014-01-15 20:00  little_w  阅读(292)  评论(0编辑  收藏  举报