题意:在$0$~$m$的范围内选一个数,使其通过$n$扇防御门(位运算)后攻击力最高,求最后攻击力最高为多少。
首先感谢LYD大佬的讲解~
言归正传,假设我们去除了$m$的限制,该怎么做呢?
我们知道位运算有一个很重要的性质:不进位
也就是说,我们在每一个位上的取值与其他位无关。
所以,我们可以暴力枚举每一位上是取$0$还是$1$,由于位数不会太大,所以可以通过。
现在,我们再看回$m$的限制
显然,我们可以在上面的基础上,再加上贪心,从高到低位枚举,如果选这一位会超过$m$或本身选1不会比选0更优,则该位选0,否则选1。
请自己证明上面做法的正确性
上代码
#include<bits/stdc++.h> using namespace std; string st;int p,c[100100],cp[100100],max,n,m,man,ans; int main() { cin>>n>>m; for (int i=1;i<=n;i++) { cin>>st>>p; if (st=="AND") c[i]=1; if (st=="OR") c[i]=2; if (st=="XOR") c[i]=3;//改字符串为数字,更好操作 cp[i]=p; } for (int i=30;i>=0;i--)//逐位枚举 { int x0=0,x1=1; for (int j=1;j<=n;j++) { if (c[j]==1) x0&=(cp[j]>>i)&1,x1&=(cp[j]>>i)&1; if (c[j]==2) x0|=(cp[j]>>i)&1,x1|=(cp[j]>>i)&1; if (c[j]==3) x0^=(cp[j]>>i)&1,x1^=(cp[j]>>i)&1; }//模拟选0与选1经过防御门后的最终值 if (x0>=x1||((man|(1<<i))>m)) { ans|=(x0<<i); } else { man|=(1<<i); ans|=(x1<<i); } } cout<<ans<<endl; return 0; }
事实上,上面枚举“过防御门”的做法其实可以做一些微小的优化,即用两个变量分别为$0$与$2^{30}-1$,再枚举“过防御门”,这样只用枚举一次“过防御门”了。