题目描述
某个充电站,可提供 n 个充电设备,每个充电设备均有对应的输出功率。
任意个充电设备组合的输出功率总和,构成功率集合 P 的 1 个元素。
功率集合 P 的最优元素,表示最接近充电站最大输出功率 p_max 的元素。
输入描述
输入为 3 行:
第 1 行为充电设备个数 n
第 2 行为每个充电设备的输出功率
第 3 行为充电站最大输出功率 p_max
备注
充电设备个数 n > 0
最优元素必须小于或等于充电站最大输出功率 p_max
输出描述
功率集合 P 的最优元素
** 示例1
输入
4
50 20 20 60
90
输出
90
说明>
当充电设备输出功率50、20、20组合时,其输出功率总和为90,最接近充电站最大充电输出功率,因此最优元素为90。
** 示例2
输入
2
50 40
30
输出
0
说明>
所有充电设备的输出功率组合,均大于充电站最大充电输出功率30,此时最优元素值为0。
** 示例3
输入
3
2 3 10
9
输出
5
说明>
选择功率为2,3的设备构成功率集合,总功率为5,最接近最大功率9。不能选择设备10,因为已经超过了最大功率9。
** 示例4
输入
3
1 2 3
5
输出
5
思路
一、问题本质
这是01背包问题的经典变种(求不超过背包容量的最大价值),核心目标是从充电设备中选择任意个,使功率总和≤充电站最大输出功率p_max,且尽可能接近p_max。
二、解法1:动态规划(迭代版,推荐)
- 状态定义:
dp[j]表示“最大允许功率为j时,能组合出的最大功率和”(即容量为j的背包可装下的最大“价值”,此处价值=功率); - 初始化:
dp数组初始化为0(无设备时,所有功率限制下的最大和均为0); - 状态转移:
- 遍历每个充电设备(共
n个),对每个设备的功率w,从p_max倒序遍历到w(避免重复选择同一设备); - 转移方程:
dp[j] = Math.max(dp[j], dp[j - w] + w)(选当前设备:dp[j-w]+w;不选:dp[j],取最大值);
- 遍历每个充电设备(共
- 结果输出:
dp[p_max]即为≤p_max的最大功率和(最优元素)。
三、解法2:记忆化搜索(递归版,辅助理解DP)
- 状态定义:
memo[i][j]表示“考虑前i个设备,最大允许功率为j时的最优功率和”,初始为-1(未计算); - 递归边界:
i=0(无设备可选)时,返回0; - 递归逻辑:
- 不选第
i个设备:最优解=dfs(i-1, j); - 选第
i个设备(仅当设备功率≤j):最优解=当前设备功率 + dfs(i-1, j-当前设备功率); - 取两者最大值,缓存结果到
memo;
- 不选第
- 结果输出:
dfs(n, p_max)即为最优解。
四、时空复杂度
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 迭代DP | O(n×p_max) | O(p_max)(一维数组) |
| 记忆化搜索 | O(n×p_max) | O(n×p_max)(二维缓存) |
代码
function solution() {
const n = Number(readline());
const list = readline().split(' ').map(Number);
const p_max = Number(readline());
const dp = Array(p_max+1).fill(0); // dp[j]表示容量为j的背包能装下的最大货物重量
for (let i = 1; i <= n; i++) {
for (let j = p_max; j >= list[i-1]; j--) {
dp[j] = Math.max(dp[j-list[i-1]] + list[i-1], dp[j]);
}
}
console.log(dp[p_max]);
}
function solution2() { // 记忆化搜索(帮助理解dp)
const n = Number(readline());
const powers = readline().split(' ').map(Number);
const p_max = Number(readline());
// 记忆化缓存:memo[i][j] 表示考虑前i个设备,最大允许功率为j时的最优解
// -1 表示未计算过
const memo = Array.from({ length: n }, () => Array(p_max + 1).fill(-1));
// 递归函数:计算前i个设备(索引0~i-1)在最大允许功率为j时的最优功率和
function dfs(i, j) {
// 边界条件:没有设备可选时,最优解为0
if (i === 0) return 0;
// 若已计算过,直接返回缓存结果
if (memo[i - 1][j] !== -1) return memo[i - 1][j];
// 当前设备的功率
const currentPower = powers[i - 1];
// 选项1:不选第i个设备,最优解等于前i-1个设备在j限制下的解
let maxSum = dfs(i - 1, j);
// 选项2:若当前设备功率不超过j,可选择该设备,最优解为当前功率+前i-1个设备在j-currentPower限制下的解
if (currentPower <= j) {
maxSum = Math.max(maxSum, currentPower + dfs(i - 1, j - currentPower));
}
// 缓存结果并返回
memo[i - 1][j] = maxSum;
return maxSum;
}
// 计算前n个设备在p_max限制下的最优解
const result = dfs(n, p_max);
console.log(result);
}
// 测试用例
const cases = [
`4
50 20 20 60
90`,
`2
50 40
30`,
`3
2 3 10
9`,
`3
1 2 3
5`
];
let caseIndex = 0, lineIndex = 0;
const readline = (() => {
let lines = [];
return () => {
if (!lineIndex) lines = cases[caseIndex].trim().split('\n').map(l => l.trim());
return lines[lineIndex++];
};
})();
cases.forEach((_, i) => {
caseIndex = i;
lineIndex = 0;
solution();
console.log('-------');
});
5万+

被折叠的 条评论
为什么被折叠?



