uva1326 - Jurassic Remains 中途相遇法

Paleontologists in Siberia have recently found a number of fragments of Jurassic period dinosaur skeleton. The paleontologists have decided to forward them to the paleontology museum. Unfortunately, the dinosaur was so huge, that there was no box that the fragments would fit into. Therefore it was decided to split the skeleton fragments into separate bones and forward them to the museum where they would be reassembled. To make reassembling easier, the joints where the bones were detached from each other were marked with special labels. Meanwhile, after packing the fragments, the new bones were found and it was decided to send them together with the main fragments. So the new bones were added to the package and it was sent to the museum.

However, when the package arrived to the museum some problems have shown up. First of all, not all labels marking the joints were distinct. That is, labels with letters `A' to `Z' were used, and each two joints that had to be connected were marked with the same letter, but there could be several pairs of joints marked with the same letter.

Moreover, the same type of labels was used to make some marks on the new bones added to the box. Therefore, there could be bones with marked joints that need not be connected to the other bones. The problem is slightly alleviated by the fact that each bone has at most one joint marked with some particular letter.

Your task is to help the museum workers to restore some possible dinosaur skeleton fragments. That is, you have to find such set of bones, that they can be connected to each other, so that the following conditions are true:

  • If some joint is connected to the other joint, they are marked with the same label.
  • For each bone from the set each joint marked with some label is connected to some other joint.
  • The number of bones used is maximal possible.

Note that two bones may be connected using several joints.

Input 

Input consists of several datasets. The first line of each dataset contains N - the number of bones (1$ \le$N$ \le$24). Next N lines contain bones descriptions: each line contains a non-empty sequence of different capital letters, representing labels marking the joints of the corresponding bone.

Output 

For each dataset, on the first line of the output print L - the maximal possible number of bones that could be used to reassemble skeleton fragments. After that output L integer numbers in ascending order - the bones to be used. Bones are numbered starting from one, as they are given in the input file.

Sample Input 

6
ABD
EG
GE
ABE
AC
BCD

Sample Output 

5
1 2 3 5 6


  找尽量多的串让每个字母出现的次数都是偶数。

  位运算真是一个神奇的东西。。把这些串转化成2进制数,问题就变成了选尽量多的数,它们异或值是0。

  1和0异或0还是本身,1异或1是0,0异或1是1。也就是说0是没有作用的,变成了1的异或,这样可以看出一些数以任意顺序异或答案都是一样的,因为每一位1的个数确定了答案就确定了。看二进制的每一位,如果有奇数个1那一位异或就是1。所以刚好异或是0的话就说明有偶数个1。

  但是如果枚举所有情况时间O(2^n),中途相遇法把时间变成了O(2^(n/2)lgn)(如果用map映射)。也就是先枚举前一半的所有情况,用map[异或值]对应前一半数这个异或值串最多的那个数,也就是1最多。利用异或运算神奇的地方,两个相等的数异或为0。因此枚举后一半数所有情况的异或值,只要map里出现过这个值,也就是前一半出现过,就满足题意,在这些情况里找最大的就是答案。

  这个为什么节省时间呢?是因为省去了前一半异或值相等但串很少的在后一半枚举。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<sstream>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define INF 0x3f3f3f3f
#define MAXN 30
#define MAXM 60
#define eps 1e-9
#define pii pair<int,int>
using namespace std;
char s[1010];
int a[MAXN];
int bitcount(int x){
    int ret=0;
    while(x){
        if(x&1) ret++;
        x>>=1;
    }
    return ret;
}
int main(){
    freopen("in.txt","r",stdin);
    int N;
    while(scanf("%d",&N)!=EOF){
        memset(a,0,sizeof(a));
        for(int i=0;i<N;i++){
            scanf("%s",s);
            for(int j=0;s[j];j++) a[i]^=(1<<(s[j]-'A'));
        }
        int n1=N/2,n2=N-n1,ans=0;
        map<int,int> Map;
        Map.clear();
        for(int i=0;i<(1<<n1);i++){
            int x=0;
            for(int j=0;j<n1;j++) if(i&(1<<j)) x^=a[j];
            if(!Map.count(x)||bitcount(Map[x])<bitcount(i)) Map[x]=i;
        }
        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(Map.count(x)&&bitcount(Map[x])+bitcount(i)>bitcount(ans)) ans=(i<<n1)^Map[x];
        }
        printf("%d\n",bitcount(ans));
        int first=1;
        for(int i=0;i<N;i++) if((1<<i)&ans){
            if(first){
                printf("%d",i+1);
                first=0;
            }
            else printf(" %d",i+1);
        }
        puts("");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值