题解:洛谷 P17012 条形蛋糕
【题目来源】
洛谷:P17012 [GESP202606 六级] 条形蛋糕 - 洛谷
【题目描述】
寒假到了,小杨同学打算找一份兼职,顺便体验一下打工人的生活。
小杨同学给一家蛋糕店发送了一份自己的简历,希望可以在寒假来这里帮忙。店长最近正好遇到了一个难题:店里每天会做一条长条蛋糕,但是不同长度的蛋糕块卖出的价格不同,应该怎么分才能卖得最多呢?
有趣的是店长曾经学习过计算机专业。他最近对动态规划算法很感兴趣,于是打算用这个问题考一考小杨同学,问题如下:
- 给定一条长度为 \(n\) 的长条蛋糕和一个价格表,该价格表表示长度为 \(i\) (\(i = 1, 2, \dots, n\)) 的蛋糕块的价格为 \(p_i\)。求蛋糕的分割方案,使得总销售价格最大,注意蛋糕块的长度必须为整数。
【输入】
第一行一个正整数 \(n\) (\(1 \le n \le 10^3\)),表示长条蛋糕的总长度。
第二行 \(n\) 个正整数 \(p_1, p_2, \dots, p_n\) (\(1 \le p_i \le 10^5\)),表示不同长度蛋糕块的价格。
【输出】
一行一个正整数,表示最大总销售价格。
【输入样例】
4
1 5 8 9
【输出样例】
10
【核心思想】
-
问题分析:给定长度为 \(n\) 的蛋糕和价格表 \(p_1, p_2, \ldots, p_n\),要求将蛋糕分割为若干整数长度的块,使得总价格最大。这是一个完全背包问题,核心在于每种长度的蛋糕块可以无限选取,在总长度限制下最大化总价值。
-
算法选择:
- 动态规划(完全背包):设 \(dp[j]\) 表示总长度为 \(j\) 时的最大价格,每种长度 \(i\) 的物品可以无限次选取
- 状态转移:\(dp[j] = \max(dp[j], dp[j-i] + p_i)\),表示在总长度 \(j\) 中选择一块长度为 \(i\) 的蛋糕
-
关键步骤:
- 初始化:\(dp[0] = 0\),其余 \(dp[j] = 0\)(正整数价格保证非负,初始化为 \(0\) 即可)
- 外层枚举物品(\(i\) 从 \(1\) 到 \(n\)):遍历每种可选的蛋糕长度
- 内层枚举容量(\(j\) 从 \(i\) 到 \(n\)):正序遍历,允许同一物品重复选取
- 状态转移:\(dp[j] \leftarrow \max(dp[j], dp[j-i] + p_i)\)
- 输出结果:\(dp[n]\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2)\),外层 \(n\) 种长度,内层最多 \(n\) 的容量
- 空间复杂度:\(O(n)\),一维 \(dp\) 数组即可,二维数组可优化为一维
-
完全背包的核心思想:
- 无限选取特性:与 \(0/1\) 背包不同,完全背包中每种物品可以选取任意多次,因此内层循环正序遍历(\(j\) 从 \(i\) 到 \(n\)),使得 \(dp[j-i]\) 可能已经包含了当前物品 \(i\),实现无限选取
- 最优子结构:总长度为 \(j\) 的最优解可以由总长度为 \(j-i\) 的最优解加上一块长度为 \(i\) 的蛋糕得到,即 \(dp[j] = \max_{1 \leq i \leq j}\{dp[j-i] + p_i\}\)
- 空间优化:一维数组即可满足需求,\(dp[j]\) 在更新时引用的是同一轮已更新的 \(dp[j-i]\),隐含了"无限选取"的语义
- 与 \(0/1\) 背包的区别:\(0/1\) 背包内层逆序遍历防止重复选取,完全背包正序遍历允许重复选取
- 适用于物品无限供应、在容量限制下最大化价值的资源分配类问题
【算法标签】
普及- #完全背包
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 1005; // 常量:最大蛋糕长度
int n; // n: 蛋糕总长度
int p[N]; // p[i]: 长度为 i 的蛋糕块的价格
int dp[N][N]; // dp[i][j]: 考虑前 i 种长度,总长度为 j 时的最大价格
int main()
{
cin >> n; // 读入蛋糕总长度
for (int i = 1; i <= n; i++) // 读入各长度蛋糕块的价格
cin >> p[i];
for (int i = 1; i <= n; i++) // 枚举考虑的蛋糕长度种类(1到i)
for (int j = 0; j <= n; j++) // 枚举当前总长度
{
dp[i][j] = dp[i - 1][j]; // 不选长度为 i 的蛋糕块
if (j >= i) // 如果当前总长度可以放下长度为 i 的蛋糕块
dp[i][j] = max(dp[i][j], dp[i][j - i] + p[i]); // 选一块长度为 i 的蛋糕
}
cout << dp[n][n] << endl; // 输出最大总销售价格
return 0;
}
【运行结果】
4
1 5 8 9
10
【算法真传】
浙公网安备 33010602011771号