zoukankan      html  css  js  c++  java
  • 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

    【完整代码】:

      1 #include<iostream>
      2 
      3 #include<stdio.h>
      4 
      5 #include<string.h>
      6 
      7 #include<algorithm>
      8 
      9 #include<stdlib.h>
     10 
     11 #include<math.h>
     12 
     13 #include<queue>
     14 
     15 #include<vector>
     16 
     17 #include<set>
     18 
     19 #include<map>
     20 
     21 #define MAXN 100+5
     22 
     23 #define MAXM 100+5
     24 
     25 #define oo 1e9
     26 
     27 #define eps 0.001
     28 
     29 #define PI acos(-1.0)//这个精确度高一些
     30 
     31 #define REP1(i,n) for(int i=0;i<=(n);i++)
     32 
     33 #define REP2(i,n) for(int i=1;i<=(n);i++)
     34 
     35 #define DREP2(i,n) for(int i=(n);i>=1;i--)
     36 
     37 #define LL long long
     38 
     39 using namespace std;
     40 
     41  
     42 
     43 int nums[MAXN];//表示统计的异或的结果
     44 
     45 int n;
     46 
     47 int p1,p2;
     48 
     49 map<int,int> Map;
     50 
     51 //int getbits(int x)
     52 
     53 //{
     54 
     55 //    int ans=0;
     56 
     57 //    while(x>0)
     58 
     59 //    {
     60 
     61 //        ans+=(1&x);
     62 
     63 //        x>>1;
     64 
     65 //    }
     66 
     67 //    return ans;
     68 
     69 //}
     70 
     71  
     72 
     73 int getbits(int x)//这个统计位数的函数值得学习
     74 
     75 {
     76 
     77     return x==0?0:getbits(x/2)+(x&1);
     78 
     79 }
     80 
     81 void readin()
     82 
     83 {
     84 
     85     memset(nums,0,sizeof(nums));
     86 
     87     char s[10005];
     88 
     89     for(int i=0;i<n;i++)
     90 
     91     {
     92 
     93         cin>>s;
     94 
     95         int len=strlen(s);
     96 
     97         for(int j=0;j<len;j++)
     98 
     99         nums[i]=nums[i]^(1<<(s[j]-'A'));//存储的是z到a的结果
    100 
    101     }
    102 
    103     return ;
    104 
    105 }
    106 
    107  
    108 
    109 void makeMap()
    110 
    111 {
    112 
    113     Map.clear();
    114 
    115     int p=n/2;
    116 
    117     for(int i=0;i<(1<<p);i++)//状态压缩,枚举每种组合情况
    118 
    119     {
    120 
    121 //        cout<<"i="<<i<<endl;
    122 
    123         int x=0;//存储每种组合异或的结果,注意初始化为0,0和任何数异或等于原数
    124 
    125         for(int j=0;j<p;j++) {if (i&(1<<j)) x=x^nums[j];}//读取每位上的数字,注意nums从1开始存储
    126 
    127         if (!Map.count(x))Map[x]=i;//查找关键字//注意i中既包含位数又包含选择信息
    128 
    129         else if (getbits(Map[x])<getbits(i)) Map[x]=i;
    130 
    131     }
    132 
    133     return;
    134 
    135 }
    136 
    137 void getans()
    138 
    139 {
    140 
    141     int ans=0;
    142 
    143     int p=n-n/2;//枚举后p位,状态压缩比搜索好写啊
    144 
    145  
    146 
    147     for(int i=0;i<(1<<p);i++)
    148 
    149     {
    150 
    151         int x=0;
    152 
    153         for(int j=0;j<p;j++)
    154 
    155             if (i&(1<<j)) x=x^nums[j+n/2];//同上
    156 
    157         if (Map.count(x))//注意:我们的目标是异或结果为0,即两个x相等
    158 
    159         {
    160 
    161             int k1=getbits(i),k2=getbits(Map[x]),m=getbits(ans);
    162 
    163             if (m<k1+k2)
    164 
    165             {
    166 
    167 //                ans=(Map[x]<<(n/2))^i;//注意好移动步数
    168 
    169                   ans=(i<<(n/2))^Map[x];
    170 
    171             }
    172 
    173  
    174 
    175         }
    176 
    177     }
    178 
    179     cout<<getbits(ans)<<endl;
    180 
    181     if (ans>0)
    182 
    183     {
    184 
    185         int cnt=0,A[105];
    186 
    187         for(int i=0;i<n;i++) if (ans &(1<<i)) A[cnt++]=i+1;
    188 
    189         for(int i=0;i<cnt-1;i++) cout<<A[i]<<" ";
    190 
    191         cout<<A[cnt-1]<<endl;
    192 
    193     }
    194 
    195  
    196 
    197     cout<<endl;
    198 
    199     return;
    200 
    201 }
    202 
    203  int main(){
    204 
    205     while(cin>>n && n)
    206 
    207     {
    208 
    209     readin();
    210 
    211     makeMap();//构建前1--n/2组合异或的MAP,用状态压缩实现
    212 
    213     getans();
    214 
    215     }
    216 
    217     return 0;
    218 
    219 }

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

  • 相关阅读:
    poj 3068 Bridge Across Islands
    XidianOJ 1086 Flappy v8
    XidianOJ 1036 分配宝藏
    XidianOJ 1090 爬树的V8
    XidianOJ 1088 AK后的V8
    XidianOJ 1062 Black King Bar
    XidianOJ 1091 看Dota视频的V8
    XidianOJ 1098 突击数论前的xry111
    XidianOJ 1019 自然数的秘密
    XidianOJ 1109 Too Naive
  • 原文地址:https://www.cnblogs.com/little-w/p/3525277.html
Copyright © 2011-2022 走看看