小扇和小船的数字游戏 - 华为OD统一考试(C卷)

OD统一考试(C卷)

分值: 200分

题解: Java / Python / C++

alt

题目描述

小扇和小船今天又玩起来了数字游戏,

小船给小扇一个正整数 n(1 ≤ n ≤ 1e9),小扇需要找到一个比 n 大的数字 m,使得 m 和 n 对应的二进制中 1 的个数要相同,如:

4对应二进制100

8对应二进制1000

其中1的个数都为1个

现在求 m 的最小值。

输入描述

输入一个正整数 n(1 ≤ n ≤ 1e9)

输出描述

输出一个正整数 m

示例1

输入:
2

输出:
4

说明:
2的二进制10,
4的二进制位100,
1的个数相同,且4是满足条件的最小数

示例2

输入:
7

输出:
11

说明:
7的二进制111,
11的二进制位1011,
1的个数相同,且11是满足条件的最小数

题解

题解分析

这道题目要求找到一个比给定的正整数 n 大的数字 m,使得它们的二进制表示中 1 的个数相同。本质上,题目要求在二进制表示中,找到一个比 n 大的具有相同数量的 1 的数。

给出的解法是使用贪心算法。首先,将 n 转换为二进制表示,然后通过贪心地找到可以将某个 0 变成 1 的位置,使得数字变大,同时保持 1 的个数不变。最后输出这个结果。

解题思路

  1. 将给定的十进制数 n 转换为二进制表示,并在末尾添加一个 0。
  2. 使用贪心算法找到可以将某个 0 变成 1 的位置的索引 idx。
  3. 调整 idx 位 0 变成 1,同时调整 idx 前的位,使得低位为 1,高位为 0,且二进制中 1 的个数不变。
  4. 输出调整后的结果。

Java

import java.util.Scanner;
import java.util.ArrayList;
/**
 * @author code5bug
 */
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        System.out.println(solve(n));
    }

    // 解决问题的函数
    public static int solve(int n) {
        ArrayList<Integer> bs = new ArrayList<>();
        // 转换为二进制
        while (n > 0) {
            bs.add(n % 2);
            n /= 2;
        }
        // 在列表末尾添加一个 0,以便处理
        bs.add(0);

        // 贪心的寻找可以将 0 变成 1 的地方的索引 idx(使得数字变大)
        // idx 满足低位存在 1 且 bs.get(idx) == 0
        int idx = 0, oneCnt = 0;
        for (; idx < bs.size(); idx++) {
            if (oneCnt > 0 && bs.get(idx) == 0) {
                break;
            }
            oneCnt += bs.get(idx);
        }

        // 将 idx 位 0 变成 1,同时 idx 前的位也相应调整
        // 调整规则:低位 1,高位 0,且二进制中 1 的个数不变
        for (int i = 0; i < idx; i++) {
            bs.set(i, (i < oneCnt - 1) ? 1 : 0);
        }

        // idx 位 0 变成 1,后面的位全部保持不变
        bs.set(idx, 1);

        // 计算最终结果
        int result = 0, base = 1;
        for (int i = 0; i < bs.size(); i++) {
            result += bs.get(i) * base;
            base *= 2;
        }

        return result;
    }
}

Python

def solve(n):
    bs = []
    # 转换为二进制
    while n > 0:
        bs.append(n % 2)
        n //= 2
    # 在列表末尾添加一个 0,以便处理
    bs.append(0)

    # 贪心的寻找可以将 0 变成 1 的地方的索引 idx(使得数字变大)
    # idx 满足低位存在 1 且 bs[idx] == 0
    idx, one_cnt = 0, 0
    while idx < len(bs):
        if one_cnt > 0 and bs[idx] == 0:
            break
        one_cnt += bs[idx]
        idx += 1

    # 将 idx 位 0 变成 1,同时 idx 前的位也相应调整
    # 调整规则:低位 1,高位 0,且二进制中 1 的个数不变
    for i in range(idx):
        bs[i] = 1 if i < one_cnt - 1 else 0

    # idx 位 0 变成 1,后面的位全部保持不变
    bs[idx] = 1

    # 计算最终结果
    result, base = 0, 1
    for bit in bs:
        result += bit * base
        base *= 2

    return result


if __name__ == "__main__":
    n = int(input())
    print(solve(n))

C++

#include <iostream>
#include <vector>

using namespace std;

int solve(int n) {
    vector<int> bs;
    // 转换为二进制
    while (n > 0) {
        bs.push_back(n % 2);
        n /= 2;
    }
    // 在数组末尾添加一个 0,以便处理
    bs.push_back(0);

    // 贪心的寻找可以 0 变成 1 的地方 idx (使得数字变大)
    // idx 满足低位存在 1 且 bs[idx] == 0
    int idx = 0, oneCnt = 0;
    for (; idx < bs.size(); idx++) {
        if (oneCnt > 0 && bs[idx] == 0) break;
        oneCnt += bs[idx];
    }

    // 将 idx 位 0 变成 1,同时 idx 前的位也相应调整
    // 调整规则: 低位 1 ,高位 0, 且 二进制中 1 的个数不变
    for (int i = 0; i < idx; i++) {
        bs[i] = (i < oneCnt - 1) ? 1 : 0;
    }

    // idx 位 0 变成 1 ,后面的位全部保持不变
    bs[idx] = 1;

    int result = 0, base = 1;
    for (int bit : bs) {
        result += bit * base;
        base *= 2;
    }

    return result;
}

int main() {
    int n;
    cin >> n;
    cout << solve(n) << endl;

    return 0;
}

🙏整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏

#面经##校招##春招##华为##面试#
全部评论
从低位往高位遍历,找到右边有1的第一个0,将这个0与1交换即可,如果所有的0右边都没有1,则直接在末尾加个0
1 回复 分享
发布于 2024-04-25 13:55 浙江
for (int i = 0; i < idx; i++) { bs.set(i, (i < oneCnt - 1) ? 1 : 0); } 这步直接用bs.set(idx-1,0)可能更好理解
点赞 回复 分享
发布于 2024-06-22 17:20 浙江

相关推荐

10-23 16:33
门头沟学院 Java
本人某中9本科,成绩中等,目前没科研没实习,目前后端学到了javaWeb,开始没定好方向,在学国外课程,走工程路线起步有点晚了,到这个时间点了还在学JavaWeb,顿感迷茫,不知道是坚持走下去还是寒假去准备考研。考研这个路弄得我还是心痒痒的,因为从众考研的人也不在少数,所以会有这方面的心理安慰吧,就是“不行我可以去考研啊”,而且意味着三年的缓冲,为了复试还有积攒经验美化简历,其实现在也可以去申入实验室打杂;就业可能意味着多些工作经验,工程岗应该到后面还是经验大于学历?还是有点迷茫了,求助好心人有无路线启发
千千倩倩:同27给点建议,现在这个时间点可以快速看完外卖和点评,不用跟着敲,但一定要在看的时候总结每个部分的整个业务流程,对其中的实现有一个大概的印象。然后直接开始看八股,刷算法。八股和算法最好还是在项目学习中穿插着看。如果计算机基础,算法这些基础好,加上每天刻苦学习,两周可以达到勉强能面试的水平,到时候就直接海投中小厂,在约面和面试的过程中不断巩固知识。没找到实习也没关系,就当积累经验。再沉淀一波直接明年三月开始投暑期,毕竟是9本,总是有面试机会的,只要你这三个月不懈怠,面试发挥得一定不错,只要拿到一个中,大厂暑期实习,秋招就有竞争力了。总得而言,现在还有机会,但是时间非常紧张,需要你结合自己情况考虑,共勉
你会选择考研还是直接就业
点赞 评论 收藏
分享
10-14 21:00
门头沟学院 Java
吃花椒的狸猫:这个人说的倒是实话,特别是小公司,一个实习生哪里来的那么多要求
点赞 评论 收藏
分享
评论
4
4
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务