题解:学而思编程 热气球升空
【题目来源】
学而思编程:热气球升空
【题目描述】
小猴想用热气球运送一些物品,一共有 \(n\) 个物品,第 \(i\) 个的体积为 \(v_i\),重量为 \(w_i\)。
小猴的热气球最多只能让重量为 \(m\) 的物体顺利升空,要运送的物品都要装到热气球的吊篮中。小猴想要在顺利升空的前提下,运送总体积最多的物品,但是更大的体积意味着更大的吊篮,吊篮体积必须大于等于要运送的物品总体积。
小猴可以使用任意大小的吊篮,一个体积为 \(V\) 的吊篮的重量是 \(V×c\)。只有物品重量加吊篮的重量总和不超过 \(m\) 时,气球才能顺利升空。
求出可以让小猴的气球升空的最大吊篮体积(这也是能运送的最大物品总体积)。
【输入】
第 \(1\) 行,\(3\) 个正整数 \(n,m,c\)
接下来 \(n\) 行,每行两个正整数 \(v_i,w_i\)
【输出】
\(1\) 行,输出最大吊篮体积。如果无法让任何物品升空,输出 \(0\)。
【输入样例】
6 80 2
6 12
11 14
7 18
5 14
4 7
10 8
【输出样例】
25
【核心思想】
-
问题分析:给定 \(n\) 个物品,每个物品有体积 \(v_i\) 和重量 \(w_i\)。热气球总重量限制为 \(m\),吊篮体积为 \(V\) 时重量为 \(V \times c\)。需要选择若干物品装入吊篮,使得物品总体积等于吊篮体积 \(V\),且物品总重量加上吊篮重量不超过 \(m\)。求最大的可行吊篮体积 \(V\)。这是一个01背包(最值型)问题,关键在于将吊篮重量成本纳入物品成本,然后求满足重量限制的最大体积。
-
算法选择:
- 01背包(最值型):
dp[j]表示凑出总体积恰好为 \(j\) 时的最小总成本(物品重量 + 吊篮重量) - 成本计算:每个物品的实际成本 = 物品重量 \(w_i\) + 物品体积 \(v_i \times c\)(因为体积为 \(v_i\) 的物品需要吊篮提供 \(v_i\) 的体积,对应吊篮重量 \(v_i \times c\))
- 逆序枚举:保证每个物品只选一次
- 01背包(最值型):
-
关键步骤:
- 初始化:
dp[0] = 0(体积为0时成本为0),其余dp[j] = INF - 01背包:
- 遍历每个物品 \(i\)
- 逆序枚举体积 \(j\) 从 \(MAXV\) 到 \(v_i\)
- 状态转移:
dp[j] = min(dp[j], dp[j - v[i]] + w[i] + v[i] * c)
- 查找答案:从大到小遍历体积 \(j\),找到第一个满足
dp[j] <= m的 \(j\),即为最大可行体积
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \times MAXV)\),\(MAXV = 5000\) 为最大体积上限
- 空间复杂度:\(O(MAXV)\),一维DP数组
-
01背包与成本转换的核心思想:
- 问题转化:将吊篮重量成本分摊到每个物品上,物品 \(i\) 的总成本 = 自身重量 \(w_i\) + 所占吊篮体积成本 \(v_i \times c\)
- 最值型01背包:
dp[j]表示凑出体积 \(j\) 的最小成本,而非最大价值 - 恰好装满:目标是找到体积恰好为 \(j\) 的方案,而非不超过 \(j\)
- 答案查找:从大到小遍历,找到第一个成本不超过限制的最大体积
- 适用于带附加成本的背包问题、资源分配类问题
【算法标签】
01背包
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// 定义长整型别名,便于处理大数据
#define int long long
// ================= 常量与全局变量 =================
const int N = 5005; // 最大体积上限
int n; // 物品数量
int m; // 总预算上限
int c; // 单位体积的附加成本系数
int sumV; // 未使用的变量(保留原样)
int ans; // 最终答案
int v[N]; // 每个物品的体积
int w[N]; // 每个物品的基础成本
int dp[N]; // 动态规划数组
// dp[j] 表示凑出总体积恰好为 j 时的最小总成本
// ================= 主函数 =================
signed main()
{
// 读取物品数量、预算上限和附加成本系数
cin >> n >> m >> c;
// 读取每个物品的体积和基础成本
for (int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
}
// 初始化 dp 数组为无穷大
memset(dp, 0x3f, sizeof(dp));
dp[0] = 0; // 体积为 0 时成本为 0
// 01 背包:每个物品只能选一次
for (int i = 1; i <= n; i++)
{
// 逆序枚举体积,保证每个物品只使用一次
for (int j = 5000; j >= v[i]; j--)
{
// 总成本 = 基础成本 + 体积 × 附加成本系数
dp[j] = min(dp[j], dp[j - v[i]] + w[i] + v[i] * c);
}
}
// 从大到小找到第一个成本不超过预算的体积
for (int j = 5000; j >= 0; j--)
{
if (dp[j] <= m) // 如果凑出体积 j 的成本不超过预算
{
ans = j; // 记录该体积
break; // 找到最大体积后退出
}
}
// 输出最大可行体积
cout << ans << endl;
return 0;
}
【运行结果】
6 80 2
6 12
11 14
7 18
5 14
4 7
10 8
25
浙公网安备 33010602011771号