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 }
|
【关键词】:位运算,中途相遇法,建模 |