题解:学而思编程 vs怪物4
【题目来源】
题目来源:vs怪物4
【题目描述】
卡罗尔和 \(1\) 只怪物在战斗,这只怪物的体力是 \(H\)
卡罗尔每次可以对 \(1\) 只怪物发起攻击,此时有以下两种情况:
(1)如果被攻击的怪物的体力为 \(1\),那么这只怪物会被消灭。
(2)如果被攻击的怪物的体力 \(X>1\),那么这只怪物会被消灭,然后会出现两只体力为 \(⌊x/2⌋\) 的新的怪物。
\(⌊r⌋\) 表示不超过 \(r\) 的最大整数。
要消灭所有怪物,最少需要多少次攻击?
【输入】
\(1\) 行,\(1\) 个正整数 \(H\)
【输出】
输出消灭所有怪物最少需要进行的攻击次数
【输入样例】
2
【输出样例】
3
【核心思想】
-
问题分析:给定怪物初始体力 \(H\),每次攻击体力为 \(X\) 的怪物时:若 \(X=1\) 则直接消灭;若 \(X>1\) 则消灭后分裂为两只体力为 \(\lfloor X/2 \rfloor\) 的新怪物。求消灭所有怪物所需的最少攻击次数。这是一个递归分治问题,具有最优子结构和重叠子问题特性。
-
算法选择:
- 记忆化搜索(Memoization):使用递归 DFS 结合哈希表存储已计算结果,避免重复计算
- 分治思想:将大问题分解为两个相同的子问题 \(f(\lfloor X/2 \rfloor)\)
-
关键步骤:
- 定义递归函数 \(dfs(x)\) 表示消灭体力为 \(x\) 的怪物所需最少攻击次数
- 边界条件:若 \(x = 1\),返回 \(1\)(一次攻击即可消灭)
- 记忆化检查:若 \(mp[x]\) 已存在,直接返回存储的结果
- 递归计算:\(dfs(x) = dfs(\lfloor x/2 \rfloor) + dfs(\lfloor x/2 \rfloor) + 1\)
- 当前攻击 \(1\) 次
- 加上消灭两只分裂怪物的攻击次数
- 将结果存入 \(mp[x]\) 并返回
-
时间/空间复杂度:
- 时间复杂度:\(O(\log H)\),每次递归 \(x\) 变为 \(\lfloor x/2 \rfloor\),递归深度为 \(O(\log H)\),每层最多计算两个分支
- 空间复杂度:\(O(\log H)\),递归栈深度和记忆化表大小均为 \(O(\log H)\)
-
记忆化搜索的核心思想:
- 重叠子问题:怪物分裂后产生的子问题可能重复出现(如 \(f(4)\) 和 \(f(8)\) 都会计算 \(f(2)\))
- 记忆化存储:使用哈希表 \(map\) 存储已计算的 \(f(x)\),避免重复递归
- 自顶向下:从初始问题 \(f(H)\) 开始,递归分解为子问题
- 适用于具有重叠子问题的递归问题,如斐波那契、树形 DP 等
【算法标签】
记忆化搜索
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
int h, ans; // 初始高度h,答案ans
map<int, int> mp; // 记忆化存储
int dfs(int x) // 递归计算函数
{
if (x==1) return 1; // 边界条件:高度为1时返回1
if (mp.count(x)) return mp[x]; // 如果已计算过,直接返回
return mp[x] = dfs(x/2) + dfs(x/2) + 1; // 递归计算
}
signed main() // 主函数
{
cin >> h; // 输入初始高度
cout << dfs(h); // 输出结果
return 0; // 程序正常结束
}
【运行结果】
2
3
浙公网安备 33010602011771号