开头先说一道2018河北省省赛的题。
神殿
icebound通过勤工俭学,攒了一小笔钱,于是他决定出国旅游。这天,icebound走进了一个神秘的神殿。神殿由八位守护者守卫,总共由64个门组成,每一道门后都有一个迷宫,迷宫的大小均为100×100。icebound在迷宫中总共耗时T小时,消耗食物K公斤。历经千辛万苦之后,icebound终于穿越了迷宫,到达了神殿的中心。神殿的中心有一个宝箱。宝箱上显示有两个正整数ll和r。icebound苦思冥想,终于发现一些打开宝箱的线索。你需要找到一个数P,它具有一个美妙的性质:它是[l,r]中所有数的二进制表示里,1的个数最多的一个数。如果你发现了这个美妙的数字,你就可以打开宝箱,获得巨额财富。
比如[4,8]中:
4: 0100 5: 0101 6: 0110 7: 0111 8: 1000
二进制表示中1的个数最多的数是7,它含有3个1。
INPUT
输入一行,两个正整数:ll和rr,用空格隔开,代表神殿中宝箱上显示的数。
1<=T<=2^21
1<=K<=10^5
1<=l<r<=2*10^9
OUTPUT
- 一个十进制数P,代表满足条件的解。如果有多个P满足条件,输出最小的P。
测试样例
4 8
样例输出
7
题意理解:
- 就是给定一个区间,找区间中二进制一最多的那个数
- 多个答案取最小值
解题思路:
这道题要是硬做的话,我目前还没有想到比较好的解决方法,搜索?转字符串?排列组合?回溯?好像听上去都可以,但是好像都不太好写,而且复杂度高的不敢想。
但是这道题如果用位运算来做的话,几乎是一道签到提。
n|(n+1)一定会比n的二进制多一个一而且还是从低位向高位加一,保证n以内就可以找到1最多且最小的那个解。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
long long l,r;
cin>>l>>r;
while((l|(l+1))<=r){
l|=(l+1);
}
cout<<l<<endl;
return 0;
}
这件事告诉我们,签到题就在那里,就看你能不能用签到的方法做了。
对于这道题,我开始也没有想到这个方法,我的确想到二进制是不是在暗示我用位运算做,但是也的确没想到为什么要n|(n+1)看完巨佬的题解才发现,这个方法简直妙啊,于是我决定把这题当作案例记下来,下次好提醒自己遇到意思位运算多想想能不能写一些这样的公式出来。
鉴于我下次可能还是想不到这种巧妙解法(其实我至今不知道这题的解法究竟是凑巧了还是真的有类似这样的板子),我决定多找一些这种题背一背。。。