【CF1661B】Getting Zero(位运算,搜索)

【题目描述】
假设有一个数 v v v,对于每次操作,你都有以下两种选择:

  • v v v变成 ( v + 1 ) % 32768 (v+1)\%32768 (v+1)%32768
  • v v v变成 ( v ∗ 2 ) % 32768 (v*2)\%32768 (v2)%32768

现在给你 n n n个整数 a 1 , a 2 , … , a n a_1,a_2,\dots, a_n a1,a2,,an,请问使得每个数变为 0 0 0的最少操作次数分别是多少?

【输入格式】
第一行输入一个整数 n ( 1 ≤ n ≤ 32768 ) n(1\le n\le 32768) n(1n32768),表示整数的数量。
第二行输入 n n n个整数 a 1 , a 2 , … , a n ( 0 ≤ a i < 32768 ) a_1,a_2,\dots, a_n(0\le a_i<32768) a1,a2,,an(0ai<32768)

【输出格式】
输出 n n n个整数,第 i i i个数表示将 a i a_i ai变为 0 0 0所需的最少操作次数。

【输入样例】

4
19 32764 10240 49

【输出样例】

14 4 4 15 

【分析】


由于 32768 = 2 15 32768=2^{15} 32768=215,因此最多将一个数乘以 15 15 15 2 2 2后对 32768 32768 32768取模即为 0 0 0,因为 ( v ∗ 2 15 ) % 2 15 = 0 (v*2^{15})\%2^{15}=0 (v215)%215=0,所以答案最大为 15 15 15
假设需要进行 i i i次加法操作, j j j次减法操作,那么需要满足 i ≤ 15 , j ≤ 15 i\le 15,j\le 15 i15,j15,最后的结果即为 i + j i+j i+j
在进行若干次乘法操作后得到的值为偶数,不能再加上一个奇数,因为那样就不可能做到模 32768 32768 32768等于 0 0 0,而如果再加上一个偶数,则一定不如先加后乘更优,例如 v → v + 1 → 2 ( v + 1 ) v\rightarrow v+1\rightarrow 2(v+1) vv+12(v+1)优于 v → 2 v → 2 v + 1 → 2 v + 2 v\rightarrow 2v\rightarrow 2v+1\rightarrow 2v+2 v2v2v+12v+2。因此可以证明一定是先进行 i i i次加法操作,之后再进行 j j j次乘法操作,即 ( v + i ) ∗ 2 j % 32768 = 0 (v+i)*2^j\%32768=0 (v+i)2j%32768=0
本题使用BFS也可以过,从当前数开始,搜索出转变成 0 0 0所需的最少步数,如果步数超过 15 15 15则剪枝。


【代码】

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int x, res = 15;
        cin >> x;
        for (int i = 0; i <= 15; i++)
            for (int j = 0; j <= 15; j++)
                if (((x + i) << j) % 32768 == 0)
                    res = min(res, i + j);
        cout << res << ' ';
    }
    return 0;
}

【BFS代码】

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int N = 33000;
int dis[N];

void bfs(int s)
{
    memset(dis, -1, sizeof dis);
    dis[s] = 0;
    queue<int> Q;
    Q.push(s);
    while (Q.size())
    {
        int t = Q.front();
        Q.pop();
        if (!t) break;
        int u = (t + 1) % 32768, v = (t << 1) % 32768;
        if (!~dis[u] && dis[t] + 1 <= 15) dis[u] = dis[t] + 1, Q.push(u);
        if (!~dis[v] && dis[t] + 1 <= 15) dis[v] = dis[t] + 1, Q.push(v);
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int x;
        cin >> x;
        bfs(x);
        cout << dis[0] << ' ';
    }
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柃歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值