解法1:
位运算只关注当前进行运算的一位,对其他位无影响。对于输入的初始攻击x,需要进行n次op运算,得到最大攻击力。把最后的输出以二进制表示,即我们希望输出的最大攻击力有尽可能多的1。
对于初始攻击x,假设一共有k位,每一位只有0或1两种取值。我们把每一位单独拿出来做n次运算。来确定该位应该填0还是1。依次从高位到低位确定。
比如:对于一个4位的数_ _ _ _,我们需要进行3次op运算,假设最高位第3位分别是0或1的两种情况,op[i]运算对应参数为t[i],则对t[i]>>3&1,这里我们只关心参数t[i]对应需要运算的一位,其他全部置0,所以与1进行与运算。0和1分别做完n次运算后的结果为res0和res1,谁大取谁,对res<<3得到最大攻击力该位的数值。其他位计算同理。
这里还有两个约束条件:
1.若res0<res1,我们需要保证当初始攻击该位取1时不超过初始攻击上限m。
2.若res0>res1,该位取0。
#include<iostream>
#include<utility>
using namespace std;
pair<string,int> a[100005];
int n, m;
int calc(int k,int now)//k表示移动的位数,now两种取值,0或1
{
for(int i=0;i<n;i++)//对x的第k位依次进行n次运算
{
int x=a[i].second >> k & 1;//将第k位移动到最低位,second表示参数t_i
if(a[i].first=="AND")
now &= x;
else if(a[i].first == "OR")
now |= x;
else
now ^= x;
}
return now;//返回第k位取0或1的结果
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
char str[5];
int t;
scanf("%s%d",str,&t);
a[i]=make_pair(str,t);
}
//计算每一位应该取0或1,将其置入最高位
int val=0,ans=0;//val用来存储当前值
for(int bit=29;bit>=0;bit--)
{
int res0 = calc(bit,0);
int res1 = calc(bit,1);
if(val+(1<<bit) <= m && res0 < res1)
{
val+=1<<bit;
ans+=res1<<bit;
}
else
ans+=res0<<bit;
}
cout<<ans<<endl;
return 0;
}
上述解法的时间复杂度为O(nlog2m)。
解法2:
我们希望结果中含有尽可能多的1,这里我们用一个全1和全0的二进制数,直接对t[i]做n次运算,若0变1,我们保留该位,1变0,不需要,若是1变1,则需要考虑初始攻击x该位取1时是否会超过上限m。
#include<bits/stdc++.h>
using namespace std;
int n , m ;
pair<string,int> a[100005];
int main()
{
bitset<40> zero , one ;
zero.reset(); // 初始化每一位都是0
one.set(); // 初始化每一位都是1
cin>>n>>m;
for(int i=0;i<n;i++)
{
string op;
int t;
cin>>op>>t;
a[i]=make_pair(op,t);
}
for(int i=0;i<n;i++)
{
if(a[i].first=="AND")
{
zero &= a[i].second;
one &= a[i].second;
}
else if(a[i].first=="OR")
{
zero |= a[i].second;
one |= a[i].second;
}
else
{
zero ^= a[i].second;
one ^= a[i].second;
}
}
int ans=0,val=0;
for(int i=0;i<=29;i++)
{
if(zero[i]==1)
ans+=(1<<i);
else if(one[i]==1 && val+(1<<i)<=m)
{
ans+=(1<<i);
val+=(1<<i);
}
}
cout<<ans<<endl;
}
时间复杂度为O(n).