题解:AtCoder AT_awc0011_e Choosing Treasure Chests
【题目来源】
AtCoder:E - Choosing Treasure Chests
【题目描述】
Takahashi is an adventurer. After exploring a dungeon, he discovered \(N\) treasure chests.
高桥是一名冒险家。在探索了一个地下城后,他发现了 \(N\) 个宝箱。
The \(i\)-th treasure chest has a weight of \(A_i\) kilograms and a value of \(B_i\).
第 \(i\) 个宝箱的重量为 \(A_i\) 公斤,价值为 \(B_i\)。
Takahashi's backpack can hold treasure chests as long as their total weight is at most \(M\) kilograms. Takahashi plans to select some number of treasure chests (possibly zero) from the \(N\) chests to put in his backpack and carry home. However, since each treasure chest exists only once, the same chest cannot be selected multiple times.
高桥的背包在总重量不超过 \(M\) 公斤的前提下可以容纳宝箱。高桥计划从这 \(N\) 个宝箱中选择若干个(可能为零)放入背包带回家。然而,由于每个宝箱只存在一个,同一个宝箱不能被多次选择。
Takahashi wants to maximize the total value of the selected treasure chests, subject to the constraint that the total weight of the selected chests is at most \(M\) kilograms. A selection that maximizes the total value is called an optimal selection. There may be multiple optimal selections.
高桥希望在所选宝箱总重量不超过 \(M\) 公斤的约束下,最大化所选宝箱的总价值。使得总价值最大化的选择称为最优选择。可能存在多个最优选择。
For each treasure chest \(i\) \((1 \leq i \leq N)\), determine whether there exists an optimal selection that includes treasure chest \(i\).
对于每个宝箱 \(i\)(\(1 ≤ i ≤ N\)),判断是否存在包含宝箱 i 的最优选择。
【输入】
\(N\) \(M\)
\(A_1\) \(B_1\)
\(A_2\) \(B_2\)
\(\vdots\)
\(A_N\) \(B_N\)
- The first line contains an integer \(N\) representing the number of treasure chests and an integer \(M\) representing the capacity of the backpack, separated by a space.
- The \((i + 1)\)-th line \((1 \leq i \leq N)\) contains an integer \(A_i\) representing the weight of the \(i\)-th treasure chest and an integer \(B_i\) representing its value, separated by a space.
【输出】
Print \(N\) lines.
On the \(i\)-th line, print Yes if there exists an optimal selection that includes treasure chest \(i\), and No otherwise.
【输入样例】
3 5
2 3
3 4
3 3
【输出样例】
Yes
Yes
No
【核心思想】
-
问题分析:给定 \(N\) 个物品(重量 \(A_i\),价值 \(B_i\))和背包容量 \(M\),求每个物品是否可能出现在某个最优解中(即是否存在一个最优选择包含该物品)。这是一个01背包 + 前缀后缀DP问题,关键在于分别计算前缀和后缀的DP数组,然后枚举中间物品的放置位置。
-
算法选择:
- 前缀DP:
f[i][j]表示前 \(i\) 个物品,容量为 \(j\) 时的最大价值 - 后缀DP:
g[i][j]表示从第 \(i\) 个到第 \(n\) 个物品,容量为 \(j\) 时的最大价值 - 枚举验证:对于每个物品 \(i\),枚举前 \(i-1\) 个物品使用的容量 \(j\),检查是否存在一种分配使得总价值等于最优值
- 前缀DP:
-
关键步骤:
- 前缀DP计算:
f[i][j] = max(f[i-1][j], f[i-1][j-A[i]] + B[i])(选或不选第 \(i\) 个物品) - 后缀DP计算:
g[i][j] = max(g[i+1][j], g[i+1][j-A[i]] + B[i])(选或不选第 \(i\) 个物品) - 获取最优值:
max_val = f[n][m] - 验证每个物品:对于物品 \(i\),枚举前 \(i-1\) 个物品使用容量 \(j\)(\(0 \leq j \leq m - A[i]\)):
- 剩余容量给后缀:
rem = m - j - A[i] - 检查是否可达最优:
f[i-1][j] + B[i] + g[i+1][rem] == max_val - 若存在这样的 \(j\),则输出
Yes,否则输出No
- 剩余容量给后缀:
- 前缀DP计算:
-
时间/空间复杂度:
- 时间复杂度:\(O(N \times M)\),前缀DP \(O(N \times M)\),后缀DP \(O(N \times M)\),验证每个物品 \(O(N \times M)\)
- 空间复杂度:\(O(N \times M)\),两个DP数组
-
前缀后缀DP的核心思想:
- 分治思想:将问题分解为前缀部分、当前物品、后缀部分
- 前缀DP:计算前 \(i\) 个物品在容量 \(j\) 下的最优值
- 后缀DP:计算从第 \(i\) 个到第 \(n\) 个物品在容量 \(j\) 下的最优值
- 组合验证:对于每个物品,枚举前缀使用的容量,检查后缀是否能补足达到最优
- 适用于需要判断每个元素是否可出现在最优解中的问题
【解题思路】

【算法标签】
01背包
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1005, M = 3005;
int n, m, a[N], b[N], f[N][M], g[N][M]; // n: 物品数量,m: 背包容量
// a: 物品重量,b: 物品价值
// f: 从前向后DP,g: 从后向前DP
signed main()
{
cin >> n >> m; // 读入物品数量和背包容量
for (int i = 1; i <= n; i++)
{
cin >> a[i] >> b[i]; // 读入每个物品的重量和价值
}
// 计算前缀DP: f[i][j] 表示前i个物品,容量为j时的最大价值
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= m; j++)
{
f[i][j] = f[i - 1][j]; // 不选第i个物品
if (j >= a[i])
{
f[i][j] = max(f[i][j], f[i - 1][j - a[i]] + b[i]); // 选第i个物品
}
}
}
// 计算后缀DP: g[i][j] 表示从第i个物品到第n个物品,容量为j时的最大价值
for (int i = n; i >= 1; i--)
{
for (int j = 0; j <= m; j++)
{
g[i][j] = g[i + 1][j]; // 不选第i个物品
if (j >= a[i])
{
g[i][j] = max(g[i][j], g[i + 1][j - a[i]] + b[i]); // 选第i个物品
}
}
}
int max_val = f[n][m]; // 最大总价值
// 检查每个物品是否为"必要的"
for (int i = 1; i <= n; i++)
{
bool flag = false; // 标记是否能达到最大价值
for (int j = 0; j <= m - a[i]; j++) // 枚举前i-1个物品使用的容量
{
int rem = m - j - a[i]; // 剩余的容量给i+1到n的物品
// 如果前i-1个物品用容量j,选第i个物品,后i+1到n个物品用容量rem能达到最大价值
if (f[i - 1][j] + b[i] + g[i + 1][rem] == max_val)
{
flag = true;
break;
}
}
if (flag)
{
cout << "Yes" << endl; // 物品i是达到最大价值的一个可选物品
}
else
{
cout << "No" << endl; // 物品i是必须物品
}
}
return 0;
}
【运行结果】
3 5
2 3
3 4
3 3
Yes
Yes
No
浙公网安备 33010602011771号