关于小菜鸡看大佬的答案分析大佬为什么这样写的文章...
D -- Find the Number
第一次做甚至理解错题目意思了,这又让我受到了不仔细看题的打击。
题目大意:
一个好数的定义:它的二进制表示中1的个数等于其末尾连续0的个数。
要求:现给出一个范围[l,r],(1<=l<=r<=1e9),找出这个范围内任意一个好数,否则输出-1.
询问次数为T(1<=T<=1e5).
下面是我参考的人的思路:
打表发现,1e9里面只有5e5个合法的数。所以我们只需要dfs出合法的数即可。
这里很多人害怕会枚举炸了,但是我们仔细想一下,只要保证dfs的过程中,没有重复的情况,那么我们最多就5e5种状态。
void dfs(vector<int>&bit, int cnt1)
其中bit表示前面的二进制,cnt1为1的个数。
if (bit.size() + cnt1 > 30)return;
因为我们需要放置0,如果这个时候二进制的长度加上1的个数(也就是0的个数)大于了30,那我们就剪枝。
主体代码如下:
vector<int>v;
int to_bit(vector<int>& b) {
int res = 0;
for (int x : b) {
res = 2 * res + x;
}
return res;
}
void dfs(vector<int>&bit, int cnt1) {
if (bit.size() + cnt1 > 30)return;
//push 1
bit.push_back(1);
dfs(bit, cnt1 + 1);
bit.pop_back();
//push 1 + 000000
bit.push_back(1);
cnt1++;
if (bit.size() + cnt1 <= 30) {
v.push_back(to_bit(bit) << cnt1);
}
cnt1--;
bit.pop_back();
//push 0
if (bit.size()) {
bit.push_back(0);
dfs(bit, cnt1);
bit.pop_back();
}
}
void slove() {
int l, r; cin >> l >> r;
auto it = lower_bound(v.begin(), v.end(), l);
if (it == v.end()) {
cout << "-1" << endl;;
}
else {
if (*it <= r)cout << *it << endl;
else cout << "-1" << endl;
}
}
下面进入正题就是我的分析:
整个dfs的最终目的就是构建符合条件的数,并且保存在数组v中。
Case1:
bit.push_back(1);
dfs(bit, cnt1 + 1);
bit.pop_back();
这里是为了添加一个“1”,继续递归,探索添加一个“1"的可能,以便构建更长的序列。
Case2:
bit.push_back(1);
cnt1++;
if (bit.size() + cnt1 <= 30) {
v.push_back(to_bit(bit) << cnt1);
}
cnt1--;
bit.pop_back();
这一部分是为了构建符合条件的数,并添加到数组 v 中,
v.push_back(to_bit(bit) << cnt1);//中先把bit转为十进制数,然后左移cnt1位(就是添加跟“1”个数相同的“0”);
Case3:
if (bit.size()) {//if中的条件是为了确保只有在已经存在至少一位中才能添加0,为了防止出现前导0
bit.push_back(0);
dfs(bit, cnt1);//因为1的个数没有变
bit.pop_back();//回溯:返回上一状态,找到更多的数
}
Case1和Case3都是在不同位置下添加”1“和”0“,递归地构建位序列,
Case1的递归优先与Case3,及每次递归都是先尝试添加”1“的序列,再处理”0“的情况。
Case 3 是在已经有 '1' 的情况下考虑添加 '0'。
这意味着 Case 3 所生成的所有序列中,必然包含前面 Case 1 生成的 1。
未完待续......