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

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

 

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 100008
int 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+委屈


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值