按位枚举确定答案, 起床困难综合症

按位枚举确定答案

在二进制数中, 高位取1所产生的数比所有低位都取1形成的数都大, 即 $ 1 << n > (1 << n) - 1 $

可以在确定答案的时候运用贪心的思想, 即能在高位取1就在高位取1

 

起床困难综合征

给定 $n$ 个运算, 每个运算中有两个参数, 操作 $ (or/xor/and) $ 和数 $(x_{i})$
求当初始数 $\leq m$ 时, 依次按顺序经过这 $n$ 次运算, 最终所能产生的最大值

 

位运算在二进制表示下不进位, 即参加位运算的各个位之间是独立无关的

所以最终所求的最大值 $ans$ 的每一位只与每个运算的数 $x_{i}$ 有关, 与其余位无关, 所以可以从高位往低位进行枚举, 依次考虑每一位应该填1还是填0

其中填1时必须满足的条件是:

  1.当前位能填1, 即填了1不会超过范围 $m$
  2.当前位填1, 最终产生的收益大于填0, 即填1后经过这 $n$ 次变换还是1, 填0后经过这 $n$ 次变换是0

否则填1不如填0, 因为因为填0能产生更高的收益( $\geq $ 填1的)并且不占用范围空间

像这样依次确定了 $ans$ 的每一位, 最终就得到了 $ans$

 

pair<string, int> ops[N];   //n次操作

//计算第bit位在经过n次运算后得到的数(1或0)
int calc(int bit, int now) {
    for(int i = 0; i < n; i++) {
        int x = ops[i].second >> bit & 1;   //取出当前数xi的第bit位
        if(ops[i].first == "AND") now &= x; //与上
        else if(ops[i].first == "OR") now |= x; //或上
        else now ^= x;  //异或上
    }
    return now;     //返回经过这n次运算后的第bit位的值(1或0)
}

//求解, 从高位向低位依次枚举, 判断应该填1还是0
void solve() {
    //pay表示初始值(当前用的数), ans表示变换后的最大值
    int pay = 0, ans = 0;
    //从29开始从大到小枚举, 因为2^30已经超过了m的范围
    for(int bit = 29; bit >= 0; bit--) {
        int ans0 = calc(bit, 0);    //当前位填0的收益
        int ans1 = calc(bit, 1);    //当前位填1的收益
        
        //判断是否满足填1的条件, 否则填0
        if(pay + (1 << bit) <= m && ans1 > ans0) pay += 1 << bit, ans += ans1 << bit;
        else ans += ans0 << bit;
    }
    cout << ans;
}

 

posted @ 2020-10-06 16:58  yikanji  阅读(133)  评论(0)    收藏  举报