当前位置: 首页 > news >正文

LeetCode 2749.得到整数零需要执行的最少操作数:很独特的一道数学题(多公式硬讲——一步步还真能看懂)

【LetMeFly】2749.得到整数零需要执行的最少操作数:很独特的一道数学题(多公式硬讲——一步步还真能看懂)

力扣题目链接:https://leetcode.cn/problems/minimum-operations-to-make-the-integer-zero/

给你两个整数:num1num2

在一步操作中,你需要从范围 [0, 60] 中选出一个整数 i ,并从 num1 减去 2i + num2

请你计算,要想使 num1 等于 0 需要执行的最少操作数,并以整数形式返回。

如果无法使 num1 等于 0 ,返回 -1

 

示例 1:

输入:num1 = 3, num2 = -2
输出:3
解释:可以执行下述步骤使 3 等于 0 :
- 选择 i = 2 ,并从 3 减去 22 + (-2) ,num1 = 3 - (4 + (-2)) = 1 。
- 选择 i = 2 ,并从 1 减去 22 + (-2) ,num1 = 1 - (4 + (-2)) = -1 。
- 选择 i = 0 ,并从 -1 减去 20 + (-2) ,num1 = (-1) - (1 + (-2)) = 0 。
可以证明 3 是需要执行的最少操作数。

示例 2:

输入:num1 = 5, num2 = 7
输出:-1
解释:可以证明,执行操作无法使 5 等于 0 。

 

提示:

  • 1 <= num1 <= 109
  • -109 <= num2 <= 109

解题方法:数学

这个题解我自己也看了很多遍,如果你想搞懂这道题,请静下心来仔细读读,相信你一定可以搞懂这道题的!哪里不懂欢迎留言。

k范围浅分析

假设num1num1num1减去kkk2i+num22^i+num22i+num2后变成了000,那么有:

num1−k∗num2=2i1+2i2+...+2iknum_1 - k * num_2 = 2^{i_1} + 2^{i_2} + ... + 2^{i_k}num1knum2=2i1+2i2+...+2ik

见到2i1+2i2+...+2ik2^{i_1} + 2^{i_2} + ... + 2^{i_k}2i1+2i2+...+2ik应该很敏感才对啊,这不是222的幂之和么,只不过加数可以重复。

令等式左边的num1−k∗num2=xnum_1 - k * num_2 = xnum1knum2=x,那么想让右边的2i1+2i2+...+2ik=x2^{i_1} + 2^{i_2} + ... + 2^{i_k}=x2i1+2i2+...+2ik=xkkk的合法范围是多少?

已知2i+1=2i+2i2^{i+1}=2^i+2^i2i+1=2i+2i,所以想让可以使等式成立的kkk比较大的话,可以把一个2i+12^{i+1}2i+1可以拆成两个2i2^i2i,最小拆成2=1+12=1+12=1+1为止。也就是说,xxx最多可以由1+1+1+⋯+11+1+1+\dots+11+1+1++1(共xxx个)组成,也就是说kkk的最大值是xxx

那么最小值呢?例如5=101(2)=22+205=101_{(2)}=2^2+2^05=101(2)=22+20kkk最小值为222。也就是说对于xxxkkk的最小值为xxx二进制下111的个数。

综上,只要popcount(x)≤k≤xpopcount(x)\leq k\leq xpopcount(x)kx,就能找到一种方法使得2i1+2i2+...+2ik=x2^{i_1} + 2^{i_2} + ... + 2^{i_k}=x2i1+2i2+...+2ik=x。(其中popcount(x)popcount(x)popcount(x)xxx二进制下111的个数,x>0x\gt 0x>0

请记住这两个条件:

  1. k≤xk\leq xkx
  2. k≥popcount(x)k\geq popcount(x)kpopcount(x)

枚举k

但是别忘了,xxx中还含有变量kkk呢!其中x=num1−k∗num2x=num_1 - k * num_2x=num1knum2。想要判断是返回kkk还是返回−1-11,还得看能不能找到一个kkk使得popcount(x)≤k≤xpopcount(x)\leq k\leq xpopcount(x)kx,其中x=num1−k∗num2x=num_1 - k * num_2x=num1knum2

最简单的办法就是暴力枚举。令kkk111开始枚举,1,2,⋯1, 2, \cdots1,2,,直到kkk超出“上界”,即k>xk\gt xk>x,也就是说k>num1−k∗num2k\gt num_1 - k * num_2k>num1knum2停止。

这里你一定会思考,现在kkk超出上界了,那么我继续增大kkk的话,待会儿kkk会不会就不超出上界了呢?

为了探究这个问题,我们把kkk的合法范围处理一下:k≤num1−k∗num2⇔k∗(1+num2)≤num1k\leq num_1 - k * num_2\Leftrightarrow k*(1+num_2)\leq num_1knum1knum2k(1+num2)num1

  • 1+num2≤01+num_2\leq 01+num20时,由于数据范围限制num1≥1num_1\geq 1num11,所以k∗(1+num2)≤num1k*(1+num_2)\leq num_1k(1+num2)num1恒成立;
  • 1+num2>01+num_2\gt 01+num2>0时,可得k≤num11+num2k\leq \frac{num_1}{1+num_2}k1+num2num1k∗(1+num2)≤num1k*(1+num_2)\leq num_1k(1+num2)num1成立,一旦kkk大于num11+num2\frac{num_1}{1+num_2}1+num2num1该等式就再也不会成立;

综上,符合假设,不考虑实际算力限制下的超时问题的话,kkk1,2,⋯1,2,\cdots1,2,枚举到k>xk\gt xk>x为止,如果存在kkk满足第二个条件即k≥popcount(x)k\geq popcount(x)kpopcount(x),我们就能找到了符合条件的kkk;否则,这个枚举范围内没有满足第二个条件的kkk就返回−1-11

那么kkk的范围究竟是多少呢?暴力枚举会不会超时呢?你别说,还真不会。

因为要想找到一个满足两个条件的kkk,除了一个限制枚举范围的k≤xk\leq xkx外,还有一个条件,就是k≥popcount(x)k\geq popcount(x)kpopcount(x)

我们一直担心的就是会不会k≤xk\leq xkx这个条件范围太大,导致kkk一直从111枚举到一个非常大的数字(甚至是无穷?),从而超时。

但是别忘了第二个条件k≥popcount(x)k\geq popcount(x)kpopcount(x)是非常容易满足的,要知道popcount(x)popcount(x)popcount(x)可是xxx在二进制下111的个数,所以先告诉你结论再去证明:kkk最大枚举到几十就会满足第二个条件了。

kkk的量级是几十(按10310^3103),numnumnum的量级是10910^9109,所以x=num1−k∗num2x=num_1 - k * num_2x=num1knum2的量级最多为101210^{12}10122402^40240已经大于101210^{12}1012了(不信可以执行下python -c "print(len(str(2**40)))"试试),xxx二进制下位数不超过404040位,就找到满足第二个条件的kkk了。

也就是说实际kkk111开始枚举,最多枚举到404040,就知道答案了,时间复杂度甚至可以认为是O(1)O(1)O(1)

  • 时间复杂度O(1)O(1)O(1)
  • 空间复杂度O(1)O(1)O(1)

然后结合实现代码说一下:

int makeTheIntegerZero(int num1, int num2) {for (int k = 1; k <= num1 - (long long)num2 * k; k++) {if (k >= __builtin_popcountll(num1 - (long long)num2 * k)) {return k;}}return -1;
}

关于这个循环的范围:

  • 如果1+num2>11+num_2\gt 11+num2>1,那么要么for循环先终止返回-1(如num1=16, num2=10会在k=2时结束for循环),要么if条件先命中return k
  • 如果1+num2≤01+num_2\leq 01+num20for循环相当于while true,但是里面的if条件一定会很快满足,并return k

总之kkk枚举不会超过404040次。解喽。

AC代码

C++
/** @Author: LetMeFly* @Date: 2025-09-05 18:29:32* @LastEditors: LetMeFly.xyz* @LastEditTime: 2025-09-05 21:33:38*/
#if defined(_WIN32) || defined(__APPLE__)
#include "_[1,2]toVector.h"
#endif/*
nums1 - k * nums2 = 2^{i_1} + 2^{i_2} + ... + 2^{i_k}
*/
class Solution {
public:int makeTheIntegerZero(int num1, int num2) {for (int k = 1; k <= num1 - (long long)num2 * k; k++) {if (k >= __builtin_popcountll(num1 - (long long)num2 * k)) {return k;}}return -1;}
};

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源

http://www.xdnf.cn/news/1461853.html

相关文章:

  • 计算机网络7 第七章 网络安全
  • Graphpad 绘图(二):小鼠生存曲线绘制与数据记录分析详解
  • Windows 部署 Gerrit 与 Apache24 配置
  • 【传奇开心果系列】Flet框架实现的搜索引擎搜索关键词建议提示和自动完成自定义组件模板特色和实现原理深度解析
  • 无人机小目标检测新SOTA:MASF-YOLO重磅开源,多模块协同助力精度飞跃
  • [特殊字符] 香蕉超市|Nano Bananary|ZHO|已开源
  • 大数据毕业设计选题推荐-基于大数据的分化型甲状腺癌复发数据可视化分析系统-Spark-Hadoop-Bigdata
  • 85 printk 输出丢失数据
  • 分布式专题——1.1 Redis单机、主从、哨兵、集群部署
  • 解决 Apache/WAF SSL 证书链不完整导致的 PKIX path building failed 问题
  • 还在为第三方包 bug 头疼?patch-package 让你轻松打补丁!
  • 时间轮算法在workerman心跳检测中的实战应用
  • leecode kadane算法 解决数组中子数组的最大和,以及环形数组连续子数组的最大和问题
  • Doirs Routine Load
  • PHP:驱动现代Web应用发展的核心力量
  • 【AI产品思路】AI 原型设计工具横评:产品经理视角下的 v0、Bolt 与 Lovable
  • 如何在 C# 中将文本转换为 Word 以及将 Word 转换为文本
  • Python 实现 Markdown 与 Word 高保真互转(含批量转换)
  • Windows 文件资源管理器无法预览文件内容word、ppt、excel、pdf
  • python创建并写入excel文件
  • Go语言的编译和运行过程
  • 【案例】AI语音识别系统的标注分区策略
  • 云计算学习笔记——日志、SELinux、FTP、systemd篇
  • FastGPT源码解析 工作流、知识库、大模型、Agent等核心代码文件梳理
  • es运维常用命令
  • 基于cornerstone3D的dicom影像浏览器 第四章 鼠标实现翻页、放大、移动、窗宽窗位调节
  • 进阶向:Python生成艺术图案(分形、数学曲线)
  • 深度相机详解
  • Spring Boot启动失败从循环依赖到懒加载配置的深度排查指南
  • 《Keil 开发避坑指南:STM32 头文件加载异常与 RTE 配置问题全解决》