起床困难综合症 Acwing 98 按位枚举

题目链接

https://www.acwing.com/problem/content/1000/

题意

N扇门,每一个门有位运算符OP和数字X,1-M选择一个数字,这个数字经过N扇门的运算后最大是多少。

思路

位运算的特性,每一位之间相互独立,互不干扰。
所以可以按位枚举初始选择,因为最高位对答案的影响最大,所以从高位向低位枚举来选择初始数字,对于初始数字单独的每一位来说,当且仅当如下情况时将设置为1

  • 这一位设置为1之后,选择的初始数字比M小
  • 这一位经过N次运算后,1的运算结果比0的大(如果相等,那么这一位选0会给后面几位更大的选择空间)

复杂度

O ( n ∗ l o g m ) O(n*logm) O(nlogm)

代码

#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=100050;
	const int inf=0x3f3f3f3f;
	struct CMD{
    
    
		string s;
		int x;
	}cmd[maxn];
	int n,m;
	//计算1和0的答案 
	int cal(int i,int now){
    
    
		for(int j=1;j<=n;j++){
    
    
			int y=cmd[j].x>>i&1;//单独取出j门x的对应位 
			if(cmd[j].s=="AND")	now&=y;
			else if(cmd[j].s=="OR")	now|=y;
			else	now^=y;
		}
		return now;
	}
	
	int main(){
    
    
		IOS
		
		cin>>n>>m;
		for(int i=1;i<=n;i++)
			cin>>cmd[i].s>>cmd[i].x;
		int ans=0;
		for(int i=30;i>=0;i--){
    
    //高位向低枚举 
			int x=1<<i;
			if(ans+x>m)//这一位不可设置为1即continue 
				continue;	
			int res1=cal(i,1),res0=cal(i,0);
			if(res1>res0)//选1比选0大,注意高位的1比高位0剩下全1还要大 
				ans+=x;
		}
		//确定初始后算一下答案 
		for(int i=1;i<=n;i++){
    
    
			if(cmd[i].s=="AND")	ans&=cmd[i].x;
			else if(cmd[i].s=="OR")	ans|=cmd[i].x;
			else	ans^=cmd[i].x;
		}
		cout<<ans<<endl;
	}
	
	

猜你喜欢

转载自blog.csdn.net/TheSunspot/article/details/108723609