例题25(侏罗纪)(中途相遇法)!

可交题的传送门

题解:好题,就是看着书上的讲解也理解了好长时间。

在代码中给出详细的解释。

代码:

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<map>
using namespace std;
const int maxn=24;
map<int ,int >table;
int solo(int x)
{
    return x==0?0:solo(x/2)+(x&1);//求对应的二进制有多少个1.
}
int n,A[maxn];
char s[1004];
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0)
            break;
        for(int i=0;i<n;i++)
        {
            scanf("%s",s);
            A[i]=0;
            for(int j=0;s[j]!='\0';j++)//a[I]记录的为每个字符串转化为二进制再转为十进制下的数。例子ABD——>1101->1011->11
                A[i]^=(1<<(s[j]-'A'));
        }
        table.clear();//table将每一个可能出现的异或值x和需要的字符串的个数形成映射。
        int n1=n/2,n2=n-n1;//上面说的,中途相遇法,我们枚举前n/2
        for(int i=0;i<(1<<n1);i++)//枚举出前n1个字符串的所有子集.例n1==5,则 00000<i<11111(二进制下)。则0或1就表示对应位的字符串是否在这个集合中出现(从后往前对应)例11001,就是编号为0,3,4的字符串构成一个集合的情况。
        {
            int x=0;
            for(int j=0;j<n1;j++)//我们找到这几个字符串。求出它们的异或值。
            {
                if(i&(1<<j))
                    x^=A[j];
            }
            if(!table.count(x)||solo(table[x])<solo(i))//如果这个异或值之前没出现过,或者出现过但所用字符串的个数小于当前的跟新table[x]
                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[n1+j];//注意,前面已经存在在n1个串,这里的j实际上是从n1开始的 ,所以异或的值是A[j+n1].
            }
            if(table.count(x)&&solo(ans)<solo(table[x])+solo(i))//如果后n2个串可以构成的x,在前面出现过,说明 这些串的异或和为0,也就是所有的字母出现的都为偶数次。更新ans。
                ans=(i<<n1)^table[x];//i前面有n1个串。
        }
        printf("%d\n",solo(ans));
        for(int i=0;i<n;i++)
        {
            if(ans&(1<<i))
                printf("%d ",i+1);
        }
        printf("\n");
    }
}

学到了如何枚举子集以及如何求元素是否在该子集出现的方法。

for(int I=0;i<(1<<n);I++)

{

for(int j=0;j<n;j++)

if(i&(1<<j))

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值