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 }

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

  • 相关阅读:
    求n的元素的最大最小值
    输出一个集合的所有子集,从长到短
    树的各种操作java
    几个笔试题目总结
    小知识不断补充
    java、C语言实现数组模拟栈
    LearnHowToThink
    Android中的this、Activity、Context等
    Android已上线应用开源分享中(第二季)
    Android已上线应用开源分享中(第一季)
  • 原文地址:https://www.cnblogs.com/little-w/p/3525277.html
Copyright © 2011-2022 走看看