洛谷 P1156 垃圾陷阱 (01背包拓展)(好题!!)

博客围绕一道类似升级版01背包的题目展开,介绍了解题思路。包括将有后效性部分作为dp维度和值,以高度为维度枚举,定义状态方程。还提及可忽略时间导致的生命值减少的离线算法,以及01背包可用滚动数组优化,对比了填表法和刷表法。

这真是一道好题目

学到了很多

一开始感觉吃或者不吃会有后效性

然后看到洛谷的题解,直接把这个有后效性的部分当作dp的维度和值

因为这个垃圾可以堆或者不堆,所以这个很像01背包,
但是加了非常多的限制条件,是一个升级版的01背包 

 


记住思考01背包问题的时候,要思考i那一维度,最后再考虑要不要用滚动数组
否则会增加思维难度

这里有几个量,是高度,生命,时间
因为时间是固定的,所以可以不理他
然后就是高度和生命谁作维度谁做值
生命没有具体的范围,不好枚举,
所以我们就拿高度为维度,来枚举,范围最大为题目给的d
f[i][j]为前i个物品高度为j的最大生命值


则f[i][j] = max(f[i-1][j] + a[i].v)  这是吃垃圾
  f[i][j] = max(f[i-1][j-a[i].h])    这是堆垃圾
注意前提是之前的状态是存在的,即不死。
如果有存在状态是j >= m(题目给的高度),那么就可以输出当前垃圾的时间
如果没有,就输出max(f[i][0])

 

然后这里有个非常非常牛逼的技巧,可以简化很多
这里的生命值是忽略时间导致生命值的减少的。(洛谷上说这是离线算法)
但是比较的时候就要和当前时间比。
如果这里生命值是算上时间导致的生命值减少,
那么比较的时候显然和0比。

那么这两者只是比较上有差别,但是如果要计算
时间的减少的话代码会多一些。

01背包一样,可以用滚动数组,把i这一维省掉

然后可以用填表法或者刷表法
其实我自己的思维习惯是填表法,即从前面
的状态推到当前状态。

但是对于这道题,显然刷表法更方便,即用现在的状态
推后来的状态

因为这里有个判断当前状态存不存在的问题
填表法的话因为从两个状态过来,所以要判断两次
但是如果是刷表法的话,只用判断当前的状态是否存在,
后来的状态等到枚举后来的状态自然会判断,只用判断一次。
大家可以自行体会一下两种方法。
 

 

填表法

#include<cstdio>
#include<algorithm>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 1123;
int f[MAXN], n, m;
struct node
{
    int t, v, h;
    void read() { scanf("%d%d%d", &t, &v, &h); }
    bool operator < (const node& rhs) const
    {
        return t < rhs.t;
    }
}a[MAXN];

int main()
{
    scanf("%d%d", &m, &n);
    REP(i, 1, n + 1) a[i].read();
    sort(a + 1, a + n + 1);
    
    f[0] = 10;
    a[0].t = 0;
    REP(i, 1, n + 1)
        for(int j = m; j >= 0; j--)
        {
            if(f[j] >= a[i].t)                    
                f[j] = max(f[j], f[j] + a[i].v);
            if(j >= a[i].h && f[j-a[i].h] >= a[i].t) 
                f[j] = max(f[j], f[j-a[i].h]);
                
            if(f[j] >= a[i].t && j == m)
            {
                printf("%d\n", a[i].t);
                return 0;
            }
        }
    printf("%d\n", f[0]);
    
    return 0;
}

 

刷表法

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 1123;
int f[MAXN], n, m;
struct node
{
    int t, v, h;
    void read() { scanf("%d%d%d", &t, &v, &h); }
    bool operator < (const node& rhs) const
    {
        return t < rhs.t;
    }
}a[MAXN];

int main()
{
    scanf("%d%d", &m, &n);
    REP(i, 1, n + 1) a[i].read();
    sort(a + 1, a + n + 1);
    
    f[0] = 10;
    a[0].t = 0;
    REP(i, 1, n + 1)
        for(int j = m; j >= 0; j--)
        	if(f[j] >= a[i].t)
        	{
        		if(j + a[i].h >= m)
            	{
                	printf("%d\n", a[i].t);
                	return 0;
           	 	}
        		f[j+a[i].h] = max(f[j+a[i].h], f[j]);
        		f[j] += a[i].v;
			}
    printf("%d\n", f[0]);
    
    return 0;
}

 

总结

(1)01背包的思考方法

(2)状态的离线设计方法

(3)填表法与刷表法的比较

转载于:https://www.cnblogs.com/sugewud/p/9819403.html

<think>好的,用户现在在找平台上关于分组背包算法单或者练习集合。根据之前的对话,用户可能已经了解过一些贪心算法和动态规划的内容,比如引用[1]里提到的Java解和引用[2]里的杨校老师课堂的背包DP练习。现在用户明确提到了分组背包,我需要先确认分组背包的基本概念,然后根据现有的引用信息,特别是引用[2]中的号列表,来提供相关的练习。 首先,分组背包是动态规划中的一种变体,特点是物品被分成若干组,每组内的物品互斥,只能选一个或者不选。这点需要明确,用户可能需要先理解这个算法,再做巩固。 引用[2]中提到的分组背包号包括P1757、P1064、P2066、P1455,这些应该是目编号。我需要检查这些号是否正确对应分组背包的问,确保没有错误。比如P1757是“通天之分组背包”,这显然是分组背包的典型目。P1064是金明的预算方案,可能需要将主件和附件视为一组,每组有多个选择。P2066是“机器分配”,可能涉及分组选择。P1455是搭配购买,可能用并查集预处理后再进行分组背包。这些目都是分组背包的经典,适合用户练习。 接下来,用户可能需要单,而不仅仅是单个目。单功能中是否有专门的背包DP或分组背包的合集呢?根据引用[2]中的信息,用户提到的杨校老师课堂的单里有分组背包的部分,但用户现在可能需要直接在上找到这些单。我需要确认是否有官方或用户创建的单包含这些目。 另外,用户可能希望得到每个目的简要说明,比如考察点和难度,这样他们可以有针对性地练习。比如P1757是模板,适合入门;P1064需要结合依赖关系进行处理,可能稍难;P1455结合并查集,是分组背包的变种应用,适合提高。这样的分类可以帮助用户逐步提升。 同时,用户可能需要一些学习建议,比如先掌握0-1背包和完全背包,再学习分组背包,以及如何高效地处理分组问。此外,注意事项如循环顺序(先枚举组,再体积,最后组内物品)也很重要,避免常见的错误。 最后,确保回答中提到的号正确,并且引导用户参考的对应目页面获取详细信息和解。如果有其他相关单,比如“算法1-6:动态规划的背包”等,也值得推荐,帮助用户系统学习。</think>根据平台的库结构和相关算法分类,以下是针对分组背包算法的推荐单及核心练习整理: --- ### 一、分组背包算法核心练习 1. **P1757 通天之分组背包** - **标签**:模板/基础训练 - **考察点**:标准分组背包实现,每组物品只能选其一 - **代码框架**: ```python for 所有组: for 体积从大到小: for 组内物品: dp[v] = max(dp[v], dp[v - w[i]] + v[i]) ``` 2. **P1064 金明的预算方案** - **标签**:依赖关系处理 - **考察点**:主件+附件构成分组,每组包含主件单独选、主件+附件1、主件+附件2等组合方式[^2] 3. **P2066 机器分配** - **标签**:分组模型转换 - **考察点**:将公司看作组,机器数量为组内选择,需输出具体方案 4. **P1455 搭配购买** - **标签**:并查集预处理 - **考察点**:先用并查集合并关联商品形成分组,再转化为分组背包 --- ### 二、推荐单路径 1. **官方单** - **算法1-6:动态规划的背包** 包含0-1背包、完全背包、多重背包、分组背包等分类练习,覆盖所有背包变种[^2] 2. **用户创建单** - **动态规划专训练**(搜索关键词:分组背包) 通常包含P1757、P1064等经典目及变种型 --- ### 三、学习建议 1. **先修知识** - 熟练掌握0-1背包(P1048)、完全背包(P1616)后再学习分组背包 2. **刷顺序** - 模板 → 依赖关系 → 预处理综合 3. **注意事项** - 循环顺序必须为 **组→体积→组内物品** - 若要求每组必选一个物品,需初始化`dp`数组为`-inf` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值