状态压缩DP codeforces 244 Problem C. The Brand New Function 和 codeforces 165 E. Compatible Numbers

来源:互联网 发布:艾宾浩斯背单词软件 编辑:程序博客网 时间:2024/06/02 02:07

解决这类题目的关键是深入了解 |, ^, ! ,& 操作的意义;

 

codeforces 244 Problem C. The Brand New Function 

题目链接: http://www.codeforces.com/contest/244/problem/C

题意: 给出n的数据,连续一段数据做|可产生一个值,问有可产生多少个不同的值?

分析: 首先对|操作进行分析; 如 a|b,就是二进制位存在1的话,该位就为1.

101101 | 010010 = 111111; 这个性质是显而易见的。那么知道这个对本题有什么作用呢?对于当前数据a来分析,

a与哪些值做|操作会产生新值呢?要和a产生新值,那么很明显就要把a中是0的为改成1.怎么改?最直观的方法就是a与前面的数一个一个|起来就是了。 呵呵,要是你就直接这样写代码的话,我也不拦你(TLE的概率几乎为100%)。其实a和前面的数做|有很多都是不必要的操作,因为a的那些位本来就是1.那么如果有一个数组记录各个位最后出现的位置,那么a是不是可以直接和这些数据做|呢? 答案是肯定的。因为中间的值不会影响产生新值。但是要注意到a必须显赫数组里最后的那个位置做|,然后依次|。


#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <set>using namespace std;#define maxn 100008int n;int a[maxn];const int full = 1<<20;bool Is[full];int pos[21]={-1};set < int> last;int Max, Min;int main( ) {    while( cin >> n ) {         for ( int i=0; i<n; ++i ) cin >> a[i];                  for ( int i=0; i<n; ++i ) {             Is[a[i] ] = true;             if(Min > a[i] ) Min = a[i];             if(Max < a[i] ) Max = a[i];                          int tmp = a[i], cnt=0;             while( tmp ) {                  ++cnt;                  if(tmp&1 ) pos[cnt] = i;                  tmp >>= 1;             }                          if(!last.empty() ){                 set<int>::iterator s=last.end();                 --s;                 tmp = a[i];                 for (; ; --s ){                     Is[tmp|=a[*s] ] = true;                     if(tmp > Max) Max = tmp;                     if(tmp < Min) Min = tmp;                     if(s == last.begin() ) break;                 }             }             last.clear();             for ( int i=1; i<=20; ++i )               if(pos[i] != -1) last.insert( pos[i] );         }         int ans = 0;         for ( int i=Min; i<=Max; ++i )  ans += Is[i];         cout << ans << endl;    }}  
代码写的很挫跑了600+ms;



codeforces 165 E. Compatible Numbers 

题目链接: http://codeforces.com/contest/165/problem/E

题意: 给出n个数据,对于每个数求和数据做&操作结果为0的数,且结果必须是数组中的数;

分析: 每个数据都是不大于4*10^6的,就是说22位就可以记录所有的状态;

假设数据为:x的结果为y,那么与x等价的数的结果也为y;什么是等价呢?

比如说; 101001 和 101000就是等价的;

dp[s^(1<<i)]  =  dp[s]  ;


#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>using namespace std;const int maxn = 1000011;const int full = (1<<22)-1;int dp[full]={-1}, a[maxn], n;int main( ) {    while( cin >> n ) {         memset(dp, -1, sizeof dp);         for ( int i=0; i<n; ++i ) {             cin >> a[i];             dp[full^a[i] ] = a[i];         }         for ( int i=full-1; i>=0; --i ) if( ~dp[i] ) {             for ( int j=0; j<22; ++j ) if(i&(1<<j) ) {                  dp[i^(1<<j)] = dp[i];             }         }         for ( int i=0; i<n; ++i ) {              if(i ) putchar(' ');              cout << dp[a[i] ];         }puts("");    }}

耗时2000+委屈