二分+位运算,LeetCode 3007. 价值和小于等于 K 的最大数字

一、题目

1、题目描述

给你一个整数 k 和一个整数 x 。整数 num 的价值是它的二进制表示中在 x2x3x 等位置处 

设置位

 的数目(从最低有效位开始)。下面的表格包含了如何计算价值的例子。
xnumBinary RepresentationPrice
1130000011013
2130000011011
22330111010013
3130000011011
33621011010102

num 的 累加价值 是从 1 到 num 的数字的  价值。如果 num 的累加价值小于或等于 k 则被认为是 廉价 的。

请你返回 最大 的廉价数字。

2、接口描述

python3
 ​
class Solution:
    def findMaximumNumber(self, k: int, x: int) -> int:
cpp
 ​
class Solution {
public:
    long long findMaximumNumber(long long k, int x) {
        
    }
};
C#
 ​
public class Solution {
    public long FindMaximumNumber(long k, int x) {

    }
}

3、原题链接

3007. 价值和小于等于 K 的最大数字


二、解题报告

1、思路分析

计算区间内所有数字的价值我们可以用数位dp计算,从而我们二分右端点即可

但是也有数学做法

给定x,[1, x]内有多少数字第0位为1? (x + 1) / 2

[1, x]内有多少数字第i位为1? 

如果x >> i为奇数,那么有 (x >> i) / 2 * 2 + (x & ((1 << i )- 1)) + 1

如果x >> i为偶数,那么有 (x >> i) / 2 * 2

为什么?

和我们考虑第0位一样,就相当于向右移i位后,看第0位的1的个数,然后低i位有1 << i种情况,累计贡献

对于x >> i奇数的情况其实就是x 在第 i 位为1,然后此时低i位的选择取决于x的低i位

2、复杂度

时间复杂度:O(log^2 k / x) 空间复杂度:O(1)

3、代码详解

python3
 ​
class Solution:
    def findMaximumNumber(self, k: int, x: int) -> int:
        def f(num: int) -> int:
            res = 0
            i = x - 1
            n = num >> i
            while n:
                res += (n // 2) << i
                if n & 1:
                    msk = (1 << i) - 1
                    res += (num & msk) + 1
                i += x
                n >>= x
            return res
        return bisect_left(range((k + 1) << x), k + 1, key=f) - 1
cpp
 ​
using i64 = long long;
class Solution {
public:
    long long findMaximumNumber(long long k, int x) {
        auto f = [&](i64 num) -> i64 {
            i64 res = 0;
            int i =  x - 1;
            for (i64 n = num >> i; n; n >>= x, i += x) {
                res += (n / 2LL) << i;
                if (n & 1) {
                    i64 msk = (1LL << i) - 1;
                    res += (num & msk) + 1;
                }
            }
            return res;
        };

        i64 lo = 0, hi = (k + 1) << x;

        while (lo + 1 < hi) {
            i64 m = (lo + hi) >> 1;
            if (f(m) <= k) lo = m;
            else hi = m;
        }

        return lo;
    }
};
C#
 ​
public class Solution {
    public long FindMaximumNumber(long k, int x) {
        long lo = 0, hi = (k + 1) << x;

        while (lo + 1 < hi) {
            long m = (hi + lo) / 2L;
            if (f(m) <= k) lo = m;
            else hi = m;
        }

        return lo;

        long f(long num) {
            long res = 0;
            int i = x - 1;
            for (long n = num >> i; n > 0; n >>= x, i += x) {
                res += (n / 2) << i;
                if (n % 2 == 1) {
                    long msk = (1L << i) - 1;
                    res += (num & msk) + 1;
                }
            }
            return res;
        }
    }
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EQUINOX1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值