2022 ACM-ICPC 网络赛(1)

关于小菜鸡看大佬的答案分析大佬为什么这样写的文章...

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。

未完待续......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值