1/16 背包笑传之测测爆
成绩(这次真爆了)
题目 | P2871 [USACO07DEC] Charm Bracelet S | P1049 [NOIP2001 普及组] 装箱问题 | P1802 5 倍经验日 | P2663 越越的组队 | P1510 精卫填海 | U524956 宠物小精灵之收服 | P2946 [USACO09MAR] Cow Frisbee Team S |
---|---|---|---|---|---|---|---|
分数 | 82 | 100 | 90 | 50 | 10 | 0 | 10 |
前置知识(有点长,知识点梳理)
dp
dp
的四步法(水字数):
- 确定状态
- 确定答案
- 确定状态转移方程
- 确定初始状态和边界
01背包
从名字上就很好理解, \(0\) 代表不选, \(1\) 代表选。
同时对应着dfs
中的选和不选问题(驭澄音)。
这种问题一般有3种写法:
- 第一种就是最简单并且本人最会的暴搜,只要数据不大,优势在我。
- 第二种就是本人最不会的贪心。这里的贪心可贪多个值
,比何坤还能贪。 - (第三种写法在后面)。
接下来我们要用贪心的角度想一下这道题:
-
按照重量贪。
容量10
物品1 物品2 物品三 重量 10 6 4 价值 1 0.1 0.1 重量贪:物品2+物品3=0.1+0.1=2
正常:物品1=10
直接过
-
按照价格贪。
容量10
物品1 物品2 物品三 重量 10 6 4 价值 10 8 4 价格贪:物品1=10
正常:物品2+物品3=8+4=12
也过
-
按照性价比贪。
容量10
物品1 物品2 物品三 重量 8 6 4 价值 10 7 4 性价比 1.25 0.86 1 性价比贪:物品1=10
正常:物品2+物品3=7+4=11
也过
看得出来,贪心的每一个方法都有hack。
不藏了,我们现在直接拿出我们的dp
吧。
温馨提示:
01背包属于多状态
dp
。时间复杂度: \(O(n*m)\)
空间复杂度: \(O(n*m)\)
题目
P2871 [USACO07DEC] Charm Bracelet S
这道题纯纯滚动背包版子。
滚动:
就意思是指把
dp
数组降一个维度。 要求就是
dp
数组只会取上一此算的数据,否则会出现“答案失踪案件” 所以还是先写完正常版本,再写滚动优化
-
确定状态:
pre[j]代表dp[i - 1][j],上一次的价值 dp[j]代表dp[i][j],这一次的价值
-
确定答案
dp[m],最后算出来的数据
-
确定状态转移方程
放不下:dp[j] = pre[j]; 放得下:dp[j] = max(pre[j], pre[j - w[i]] + val[i]);
-
确定初始状态和边界
dp和pre都为0
P1049 [NOIP2001 普及组] 装箱问题
这个比上面那题更简单,最大值问题。
-
确定状态:
dp[i - 1][j]代表上一次的占地空间 dp[i][j]代表这一次的占地空间
-
确定答案
求出最后一次的最大值,用容量减去最大值算出来的剩余空间为答案
-
确定状态转移方程
放不下:dp[i][j]=dp[i-1][j]; 放得下:dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+w[i]);
-
确定初始状态和边界
dp都为0
P1802 5 倍经验日
这个就是普通01背包板子加上不取的经验,再乘5被就行了
要开long long
!!
要开long long
!!
要开long long
!!
-
确定状态:
dp[i - 1][j]代表上一次的经验 dp[i][j]代表这一次的经验
-
确定答案
和模板一样,只需要输出最后一次的经验值就可以了(要乘5倍)
-
确定状态转移方程
放不下:dp[i][j]=dp[i-1][j]+l[i]; 放得下:dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+w[i]);
-
确定初始状态和边界
dp都为0
P2663 越越的组队
这个跟上面的一样,都是01背包的改版。
就是需要到这求,只需要求一般就可以了。
-
确定状态:
dp[i - 1][j]代表上一次的成绩 dp[i][j]代表这一次的成绩
-
确定答案
和模板一样,只需要输出最后一次的经验值就可以了(要乘5倍)
-
确定状态转移方程
放不下:dp[i][j] = dp[i - 1][j]; 放得下:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - a[i]] + a[i]);
-
确定初始状态和边界
dp全都为0
P1510 精卫填海
这道题可以用两种方法来解。
这道题十分杂,像“大乱炖”
方法一:按照最小值问题处理
-
确定状态:
dp[j]=dp[i][j]; pre[j]=dp[i-1][j];
-
确定答案
求出最后一次的最小值,然后判断,小于c就输出Impossible,大于c就输出c-ans
-
确定状态转移方程
放不下:dp[i][j] = dp[j] = pre[j]; 放得下:dp[j] = min(pre[j], pre[j - w[i]] + val[i]);
-
确定初始状态和边界
都要赋值为最大值,出来[0][0]赋值为0。 最大边界为v+n的最大范围
方法二:按照最大值问题处理
-
确定状态:
dp[j]=dp[i][j]; pre[j]=dp[i-1][j];
-
确定答案
求出每一次的最大值,然后判断是否等于-1,等于就输出Impossible,不等于就输出ans
-
确定状态转移方程
放不下:dp[j] = pre[j]; 放得下:dp[j] = max(pre[j], v[i] + pre[j - w[i]]);
-
确定初始状态和边界
dp都为0,不需要赋值
U524956 宠物小精灵之收服
经典的两个限制条件,求解最大值的问题。
-
确定状态:
dp[j][k]=dp[i][j][k]; pre[j]=dp[i-1][j][k];
-
确定答案
最后一次和最后一个的值相比,然后判断是否等于,等于就输出。
-
确定状态转移方程
放不下:dp[j][w] = pre[j][w]; 放得下:dp[j][w] = max(pre[j][w], pre[j - v1[i]][w - v2[i]] + 1);
-
确定初始状态和边界
dp不用赋值。
最后一题
没写。