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

Kimi LeetCode 3621. 位计数深度为 K 的整数数目 I Java实现

LeetCode 3621. 位计数深度为 K 的整数数目 I

题目描述

给定两个整数 `n` 和 `k`。对任意正整数 `x`,定义序列:
- `p₀ = x`
- `pᵢ₊₁ = popcount(pᵢ)`(二进制中 1 的个数)

序列最终会到达 1。`x` 的 popcount-depth 定义为使得 `p_d = 1` 的最小非负整数 `d`。

求 `[1, n]` 中 popcount-depth 恰好等于 `k` 的整数个数。

约束: `1 ≤ n ≤ 10¹⁵`,`0 ≤ k ≤ 5`

---

解题思路

关键观察:
- `D(1) = 0`(1 本身就是终点)
- `x > 1` 时,`D(x) = 1 + D(popcount(x))`
- 因此 `D(x) = k` 等价于 `D(popcount(x)) = k - 1`

算法步骤:
1. 预处理深度:对 `1 ~ 60` 的所有数预计算 popcount-depth(`n ≤ 10¹⁵` 的二进制最多 50 位)
2. 确定目标 popcount:找出所有满足 `D(c) = k - 1` 的 `c`
3. 数位 DP:统计 `[1, n]` 中二进制恰好有 `c` 个 1 的数的个数
4. 边界处理:`k = 0` 时只有 `x = 1`;`k = 1` 时需排除 `x = 1`(因为 `D(1) = 0`)

时间复杂度: `O(log² n)`

空间复杂度: `O(log² n)`

---

Java 实现

```java
import java.util.*;

class Solution {

// 计算一个数的 popcount(二进制1的个数)
private int popcount(int x) {
int cnt = 0;
while (x > 0) {
cnt += (x & 1);
x >>= 1;
}
return cnt;
}

// 计算 popcount-depth
private int getDepth(int x, int[] memo) {
if (x == 1) return 0;
if (memo[x] != -1) return memo[x];
memo[x] = 1 + getDepth(popcount(x), memo);
return memo[x];
}

// 数位DP:统计 [0, n] 中二进制恰好有 targetOnes 个1的数的个数
// s 是 n 的二进制字符串
private long dfs(int pos, int ones, boolean tight, String s, int targetOnes, Long[][] memo) {
if (ones > targetOnes) return 0; // 1的个数已超,剪枝
if (pos == s.length()) {
return ones == targetOnes ? 1 : 0;
}

int tightFlag = tight ? 1 : 0;
if (!tight && memo[pos][ones] != null) {
return memo[pos][ones];
}

int limit = tight ? (s.charAt(pos) - '0') : 1;
long res = 0;

for (int d = 0; d <= limit; d++) {
res += dfs(pos + 1, ones + d, tight && (d == limit), s, targetOnes, memo);
}

if (!tight) {
memo[pos][ones] = res;
}
return res;
}

public long popcountDepth(long n, int k) {
// k = 0:只有 1 的深度为 0
if (k == 0) {
return n >= 1 ? 1 : 0;
}

// 预处理 1~60 的深度(n <= 1e15 < 2^50,popcount 最大为 50)
int[] depthMemo = new int[65];
Arrays.fill(depthMemo, -1);
depthMemo[1] = 0;

// 找出所有 popcount 值 c,使得 D(c) = k - 1
List<Integer> targetPopcounts = new ArrayList<>();
for (int i = 1; i <= 60; i++) {
if (getDepth(i, depthMemo) == k - 1) {
targetPopcounts.add(i);
}
}

if (targetPopcounts.isEmpty()) {
return 0;
}

// 将 n 转为二进制字符串
String s = Long.toBinaryString(n);
long ans = 0;

for (int c : targetPopcounts) {
// 记忆化数组:memo[pos][ones],仅非 tight 状态有效
Long[][] memo = new Long[s.length() + 1][c + 1];
ans += dfs(0, 0, true, s, c, memo);
}

// k = 1 的特殊处理:popcount = 1 的数包括 1, 2, 4, 8...
// 但 D(1) = 0,不应计入 k=1,所以减去 1
if (k == 1) {
ans -= 1;
}

return ans;
}
}
```

---

代码要点

要点 说明
深度预处理 `getDepth` 用记忆化递归计算,利用 `D(x) = 1 + D(popcount(x))`
数位 DP 按二进制位从高到低枚举,`tight` 控制是否受 `n` 限制
非 tight 记忆化 只有 `tight = false` 时结果可复用,用 `Long[][]` 存储
k = 1 修正 `popcount = 1` 包含 `x = 1`,但其深度为 0,需减去
返回值类型 用 `long`,因为 `n` 最大时答案可能很大

如需其他语言版本(Python/C++/Go)或更详细的数位 DP 推导,可以继续问我。

http://www.jsqmd.com/news/1216666/

相关文章:

  • 2026全国大学生嵌入式竞赛:ESP32-P4/S3边缘AI与物联网开发指南
  • GICD_IROUTER寄存器:嵌入式多核系统中断路由与性能优化实战
  • 给 AI Chat 加上长期记忆:模型提取 + 程序把关 + 规则召回
  • Minecraft RPG服务器新手入门:从客户端配置到躺平变强攻略
  • 2026年7月最新广州番禺区市桥街道亨得利官方名表服务中心电话公示 - 亨得利官方
  • Android应用签名机制详解与安全实践
  • 劳力士中国官方售后服务中心|全部网点地址及电话权威信息通知(2026年7月更新) - 劳力士服务中心
  • OpenClaw技能生态与十大必备Skills安装指南
  • Vue计算属性详解:原理、用法与性能优化
  • 股票复盘工具推荐:把资料、财报和记录整理成可复用的研究流程
  • 嵌入式系统开发实战:SYSC与WUGEN寄存器配置详解
  • 空中熊猫官方网站www.kzxm.cn官方电话15108405949空中熊猫无人机 - 资讯焦点
  • 老龄化社会挑战与银发经济机遇分析
  • iOS集成支付宝支付全流程解析与最佳实践
  • 构建实时闭环CNC系统:工业传感器+边缘AI+PLC硬连接
  • 【YOLO26多模态涨点改进】ECCV 2026 | 特征融合改进篇 | 引入SFS-FF分流频率-空间特征融合模块,实现了基于结构信息的频域-空间信息聚合,助力多模态融合目标检测任务,有效涨点
  • 安防摄像头的数字孪生
  • 反思日志:结构化思考与认知复利的实践指南
  • AWS云安全实战:六年生产环境验证的六大核心控制点
  • 电容器动态分析:电压不变与电量不变模型全解析
  • 单窗口单IP搭建适配:独享云代理IP服务全维度解析 - 互联网科技品牌测评
  • 2026年北京地区优质设备回收服务商综合实力盘点 - 品牌鉴赏官2026
  • 嵌入式深度精讲|STM32引脚全方位底层原理、配置逻辑、硬件规范、高频面试题
  • 观鸟活动中的英语学习:词汇构建与实战技巧
  • 宝玑中国官方售后服务中心|服务热线及门店详细地址权威信息通告(2026年7月更新) - 亨得利钟表维修中心
  • Boost智能指针深度解析:从RAII原理到C++内存管理实战
  • Android回调事件处理机制详解与实战
  • Steam创意工坊跨平台下载解决方案:WorkshopDL使用深度指南
  • C++笔记之IO相关概念详解
  • 鸿蒙 ArkTS 实战:Route Maze 从路线迷宫到交互闭环完整解析