最近在补全提高课所有题目的题解,宣传一下汇总的地方提高课题解补全计划
题目描述
共有 $N$ 块魔法石
每块魔法石,吃掉的时间是 $s_i$,价值是 $w_i$,每单位时间内损失的能量是 $l_i$
规定,每块魔法石刚开吃的时候就会立刻获得他的价值(不会在吃的过程中损失能量)
每块魔法石一旦开吃就不能停下,魔法石能量损失到 $0$ 以后不会额外损失
求借一种方案,使得最终获得的 总价值 最大
分析
本题抛开 魔法石会损失能量 的限制,就是一道 01背包 的裸题
但是以往的 01背包 每个物品的价值是不变的,因此我们可以指定任意的次序进行 线性dp 求解
但是本题有 魔法石会损失能量 这一限制
因此 如何枚举物品的次序 是首要解决的问题,然后才可以套用 01背包模型 求解
我们这里采用 贪心思路 ,从分析 贪心解 中两个 相邻物品 的次序来入手
假设在 贪心解 中存在 $stone_i, stone_j$ 满足 $j = i + 1$ 且枚举到 $stone_j$ 时,能量都不为 $0$
如果能量为 $0$ ,那么他们的次序可以任意安排
则存在如下不等式(次序 $\overline{\mathrm{x~x~}i~j\mathrm{~x~x}}$ 获得的价值 大于等于 $\overline{\mathrm{x~x~}j~i\mathrm{~x~x}}$ 获得的价值)
$$
E_i + E_j - S_i \times L_j \ge E_j + E_i - S_j \times L_i \\\
S_j \times L_i \ge S_i\times L_j
$$
于是对于最优解中所有 $S_j \times L_i \lt S_i\times L_j$ 的物品次序,我们都可以通过一次 邻项交换 的操作变成我们上述的次序,且保证该次交换完成后,总价值不减少
于是得以证明出 贪心解 $\ge$ 最优解
而由于 贪心解 本就是 合法解 ,因此自然而然 贪心解 $\le$ 最优解
得证:贪心解 $=$ 最优解
于是我们可以直接用 结构体 存储每个物品的三个 属性 ,接着按照上述的 次序 从小到大排序
就会获得能够达到 最优解 的物品次序,然后就是经典 01背包线性DP 求 最大价值 问题
闫氏DP分析法
$f(i, j)$— 状态表示(集合):考虑前 i 个魔法石,且吃掉最后一个魔法石后,所用总时间恰好为 j 的方案
$f(i, j)$— 状态表示(属性):方案的总价值 最大 MAX
$f(i, j)$ — 状态计算:
- 吃掉第 i 个魔法石 — $f(i - 1,j - s_i) + e_i - (j-s_i)\times l_i$
- 不吃第 i 个魔法石 — $f(i-1, j)$
Code
时间复杂度: $O(N \times \sum s_i)$
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 1e5 + 10;
int n, m;
struct Node
{
int s, e, l;
bool operator< (const Node& t) const
{
return s * t.l < t.s * l;
}
}a[N];
int f[M];
void solve()
{
//求恰好的背包DP,为了保证状态都是从起点转移的,要把非起点初始化为无穷大以避免转移
memset(f, -0x3f, sizeof f);
f[0] = m = 0;
cin >> n;
for (int i = 1; i <= n; ++ i) cin >> a[i].s >> a[i].e >> a[i].l, m += a[i].s;
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++ i)
{
for (int j = m; j >= a[i].s; -- j)
{
int pre = j - a[i].s;
f[j] = max(f[j], f[pre] + a[i].e - pre * a[i].l);
}
}
int res = 0;
for (int j = 0; j <= m; ++ j) res = max(res, f[j]);
cout << res << endl;
}
int main()
{
int T = 1, t = 1;
cin >> T;
while (T -- )
{
cout << "Case #" << t ++ << ": ";
solve();
}
return 0;
}
为什么这样错了?
已经找出了一个错误,重定义里面改为
s * sto.l < sto.s * l
,但是还是错误/kk为什么yls的代码不用memset而这里要用呢
y总的是二维没优化
“如果能量为0,那么他们的次序可以任意安排” 这句话要咋理解呀
吸收一个能量石需要时间,所以初始时,能量石的次序会影响物品的价值
但是如果吸收完前 $i$ 个能量石后,剩下的 $n-i$ 个能量石的剩余能量为 $0$
则他们的次序,就不会影响最终的方案了(怎么放都是 $0$
就是dp的时候碰到那种变成零的石头 会在dp中隐式地不取它是吗
不是不取他,这里还没到dp部分,还在贪心排序的部分
证明如果为0,次序可以任意安排,是为了证明我们的这种排序方法也可以是最优解
终于懂了!太感谢了!这题我想了好久
大佬 不好意思 我想再问一个小问题
假如我们对一堆石子进行操作时 可以保证n个中的x个能量不降低成0 不过吃的该顺序没有按照s/l最小进行排序 假定这x个石子中的第i个和第i+1个不满足s/l从小到大的顺序 并且我们在对i和i+1进行交换的时候 发现原先第i个石子的能量降低成0了 对于该情况 该怎么办
$$ E_i + E_j - S_i \times L_j \ge E_j + E_i - S_j \times L_i $$
根据你上述情形,不等式左侧 $E_j - S_i \times L_j < 0$
而不等式左侧实际应该是 $E_i + 0$,却因为我们上述排序加了一个负数,我们设为 $k(\ge 0)$
既然 $E_i - k \ge E_j + E_i - S_j \times L_i$
则必然 $E_i \ge E_j + E_i - S_j \times L_i$,对于上述排序依然有效~
好的~谢谢!
怎么判断f[i,j]是表示价值恰好为j 还是价值不超过j
有点不理解。照理来讲,背包问题其实本质是选择出一个最优的子序列。难道不会存在这样的一个情况:当我们交换之后,子序列的选法也会发生变化(由于有时间消耗,导致原序列也会发生改变,做最优决策的时候就会导致各个物品的选法不同)。也就是说交换邻项的操作实际上是会影响其他位置的决策的。
所以,这个证明我感觉有一些问题。或者说,是我哪里欠考虑了。
确实,我也是这样想的,就是因为我们吃某一块能量石时,所有没吃过的能量石都会损失能量,所以我想的办法时尽量让损失的能量变少,假设没有能量损失的话,我们的总能量无疑就是所有能量的总和,而现在有了能量损失,为了获得尽可能多的能量,我们要想办法减少能量损耗,所以我们不妨只考虑没有吃过的能量块,现在要从中选择第i块来吃,那么我们吃完后剩余的能量为E-Ei-Si(所有石头L总和-之前选过的石头L总和)+SiLi,其中E为选择时剩余能量石能量的总和,Ei,Si,Li均是针对选择的能量石而言,本着贪心的原则,我们应该选择Ei+Si(所有石头L总和-之前选过的石头L总和)-SiLi最小的那一块石头,然后就没有然后了,就卡住了hh~
有没有一种可能,任意两项按照相对顺序选取就是最优解,具体可以看贪心式子,这个sort相当于把整个数组都按照这个键值排序了,所以优先考虑靠前的是必然不会导致答案变差的
励志走遍每一个彩铅哥的每一个题解
彩铅巨巨,这里为什么不用写
f[pre] + max(0,a[i].e - pre * a[i].l)
呢因为两者是等效的,这里分类讨论:
1.取max:如果后面石头还有能量剩余,那么意味着此时吃这个石头我不仅不会得到收益,还会影响我后面石头的收益(吃当前石头会耗费时间,使后面石头能量减少),所以此时就不会吃当前石头了;而如果后面石头剩余能量已经都为零了,那此时取max就意味着我吃了当前石头但当前石头收益为0,不影响答案;
2.不取max:意味着我吃了当前石头非但每收益,还会有损益,那么无论什么情况我都不会吃当前石头了
总结:两者对应实际含义不同,但在本题中结果是相同的。拓展一下,如果给定最大吃能量石时间问吃到最多能量的情况下最多能吃多少石头那么就必须取max了
是不是含在口中的能量不会随时间逐渐递减啊???
是的
这题为什么不能用不超过来实现呢,如果用不超过来实现,最后是不是就不用枚举res了
都可以,用“恰好的”来定义的话,对于这题的理解思维度较小
如果用“不超过”来定义的话,还用到了一个贪心思路,如果当前未处于进食状态,就必定会尽可能去选择石头进行食用
对于本题来说都可
%%%%大佬请问如果用不超过这题怎么写
但是不知道为什么是恰好
感觉这样简单些,其实两种都可以
这个代码的意思感觉也是恰好时间,因为如果是不超过最后不用取max的,直接输出f[n][[m]就行了,我觉得不能用不超过的原因是这里需要确定精准的知道你吃第i块等待的时间,而如果是选择使用不超过的话是没办法精确知道的
还需要初始为负无穷吗, 不初始化有什么问题嘛
和 状态的定义 有关,这个 状态定义 是 恰好,如果是 不超过 的话可以只初始化成 $0$ 即可
关于初始化的问题,只需要观察状态转移的起点就好了
不超过 可以把所有状态作为初始状态,但 恰好 必须是 $0$ 作为初始状态
这个我初始化成0也可以AC, 下次注意😹
状态转移的起点难道不都是0吗?
这题初始化成0没问题,是因为每块能量石的能量一定是非负数,所以不会出现为了满足恰好是j的情况下必须选一个负数的情况,而如果是要选择体积不超过j的最大值,那我宁愿一个能量石都不选也不选那个能量是负数的石头,所以初始化成0是可以的