leetcode397分支限界法c++
时间: 2025-04-27 19:22:29 浏览: 33
<think>好的,我现在需要解决用户关于LeetCode 397题“整数替换”的问题,特别是使用分支限界法的C++实现。用户已经提供了几个引用,包括递归、动态规划和位运算的解法,但需要的是分支限界法。首先,我需要理解分支限界法的基本概念,以及如何将其应用到这个问题中。
分支限界法通常用于解决组合优化问题,通过扩展当前节点的子节点,并估算这些子节点的下界(或上界),优先处理更有希望的分支,以尽快找到最优解。常用的数据结构是优先队列,每次选择当前最优的节点进行扩展。
接下来,我需要分析问题本身。题目要求将正整数n通过特定操作转换为1,最少的步骤数。操作包括:偶数时除以2,奇数时可以选择加1或减1。传统的递归方法可能会有重复计算,而分支限界法则可以通过优先级队列来优化选择顺序,减少不必要的计算。
然后,我需要考虑如何构建状态节点。每个节点应包含当前的数值和已用步数,同时需要一个预估的下界来指导优先队列的选择。预估的下界可以是当前数值的二进制位数,因为每次除以2相当于减少一位,所以剩余的步数至少需要log2(n)次。但需要验证这个估计是否合理。
接着,考虑如何实现优先级队列。在C++中,可以使用优先队列(priority_queue),但需要自定义比较函数,使得下界更小的节点优先被处理。同时,为了避免重复处理相同的数值,需要一个哈希表来记录已经处理过的最小步数,如果遇到相同数值但步数更多的情况,可以剪枝。
然后,处理步骤大致如下:
1. 初始化优先队列,将初始节点(n, 0)加入队列。
2. 进入循环,取出当前最优节点。
3. 如果当前数值为1,返回步数。
4. 生成子节点(偶数则一个子节点,奇数则两个子节点)。
5. 对每个子节点,计算预估下界(步数 + 剩余所需步数估计),并检查是否已经存在更优的解,如果没有则加入队列并更新哈希表。
需要注意的是,当处理奇数时,加1或减1可能导致数值变大,比如当n=3时,减1得到2,而加1得到4,但减1的步骤更少。此时,预估函数需要准确,否则可能影响效率。
另外,测试用例中可能存在大数,比如2^32-1,使用普通的整数类型可能会溢出,所以需要用unsigned或者更大的类型来处理。
现在,编写代码的大致结构:
- 定义节点结构体,包含当前值、当前步数、预估总步数。
- 优先队列按预估总步数从小到大排序。
- 哈希表记录每个数值的最小步数。
- 循环处理队列中的节点,直到找到解。
预估剩余步数时,可以采用log2(current)作为估计,但需要转换为整数。例如,current的二进制位数减1,即剩余步数的下限。这样,总预估步数为当前步数加上这个下限。
在实现中,需要处理current为1的情况,并且当current为奇数时,生成两个子节点。同时,要检查子节点的值是否已经存在于哈希表中,且步数是否更小,否则剪枝。
可能存在的问题是,分支限界法在处理大数时是否效率足够,或者是否有更优的预估函数。需要参考已有的解法,比如位运算方法,来验证预估函数的合理性。
最后,测试代码是否能够正确处理各种情况,如n=8(3步)、n=7(4步)、n=3(2步)以及大数情况,确保没有溢出和正确的结果。</think>### LeetCode 397 整数替换的分支限界法实现(C++)
#### 算法思路
分支限界法通过**优先级队列**选择当前最优路径,配合**剪枝策略**避免重复计算。每个节点的优先级由已用步数 + 剩余步数下界决定,剩余步数下界可通过二进制位数估算(例如:$n$ 的二进制位数减1)。
#### C++代码实现
```cpp
#include <queue>
#include <unordered_map>
using namespace std;
struct Node {
unsigned val;
int steps;
int estimate; // steps + lower_bound(remaining steps)
Node(unsigned v, int s, int e) : val(v), steps(s), estimate(e) {}
bool operator<(const Node& other) const {
return estimate > other.estimate; // 小顶堆
}
};
class Solution {
public:
int integerReplacement(int n) {
if (n == 1) return 0;
priority_queue<Node> pq;
unordered_map<unsigned, int> visited;
auto getEstimate = [](unsigned val, int steps) {
int bits = 0;
unsigned tmp = val;
while (tmp > 1) {
bits++;
tmp >>= 1;
}
return steps + bits; // 剩余步数下界 = 二进制位数 - 1
};
pq.push(Node(n, 0, getEstimate(n, 0)));
visited[n] = 0;
while (!pq.empty()) {
Node curr = pq.top();
pq.pop();
if (curr.val == 1) return curr.steps;
if (curr.val % 2 == 0) { // 偶数情况
unsigned next = curr.val / 2;
int new_steps = curr.steps + 1;
if (!visited.count(next) || new_steps < visited[next]) {
visited[next] = new_steps;
pq.push(Node(next, new_steps, getEstimate(next, new_steps)));
}
} else { // 奇数情况,生成两个分支
vector<unsigned> next_vals = {curr.val + 1, curr.val - 1};
for (unsigned next : next_vals) {
int new_steps = curr.steps + 1;
if (!visited.count(next) || new_steps < visited[next]) {
visited[next] = new_steps;
pq.push(Node(next, new_steps, getEstimate(next, new_steps)));
}
}
}
}
return -1;
}
};
```
#### 关键点说明
1. **节点优先级计算**:使用 `steps + log2(val)` 的下界估算,确保优先处理更接近目标的路径[^2]。
2. **剪枝优化**:通过哈希表记录已访问值的最小步数,避免重复计算[^2]。
3. **大数处理**:使用 `unsigned` 类型防止溢出(如测试用例 $2^{31}-1$)[^3]。
#### 复杂度分析
- 时间复杂度:$O(b \cdot \log b)$,其中 $b$ 是分支数,优先队列操作复杂度为 $O(\log b)$。
- 空间复杂度:$O(k)$,$k$ 为哈希表存储的中间状态数。
阅读全文
相关推荐













