背包问题(01背包与完全背包)
dp考虑两个方面,包括如何表示状态(维度,属性(min、max、cnt)),如何计算当前状态(状态转移方程)。dp问题的优化一般是对状态转移方程进行等价变形。
01背包问题
有n个物品和一个容量为V的背包。
每个物品有两个属性,包括所占用的体积v以及拥有的价值w,每件物品只能用一次。
求背包能装得下的情况下所能拥有的最大价值为多少。
f[i, j] = max(f[i-1, j], f[i, j-v] + w
滚动数组优化
- 能发现,
f[i][j]
的状态转移仅使用到了f[i-1][...]
,故可以采用滚动数组来做。即当前层的状态转移仅与上一层有关 - 当前层是
i & 1
,上一层是i-1 & 1
完全背包问题
与01背包的区别在于,每件物品可以拿无数次。
优化过程
f[i, j] = max(f[i-1, j], f[i-1, j-v]+w, f[i-1, j-2v]+2w, f[i-1, j-3v]+3w ...)
对比:
f[i, j-v] = max(f[i-1, j-v], f[i-1, j-2v]+w, f[i-1, j-3v]+2w ...)
也就是
f[i][j] =max(f[i-1][j], f[i-1][j-v]+w, f[i-1][j-2v]+2w, f[i-1][j-3v]+3w,...)
f[i][j-v] =max( f[i-1][j-v], f[i-1][j-2v]+w, f[i-1][j-3v]+2w, f[i-1][j-4v]+3w,...)
f[i][j-v]+w=max( f[i-1][j-v]+w, f[i-1][j-2v]+2w, f[i-1][j-3v]+3w, f[i-1][j-4v]+4w,...)
1)通过两项对比,可以得出结论,可省掉一重循环
f[i, j] = max(f[i][j], f[i][j-v]+w)
所以实际上多重背包的本质也还是01背包
但是01背包问题为从后往前面推导,完全背包问题为从前往后推导
2)同时,对状态存储进行优化,还可以省略掉一维数组
f[j] = max(f[j], f[j-v]+ w)
3)完全背包问题实际上求得的是某一个数组区间内的最大值,也可以视作滑动窗口最大值,对于该问题即可利用单调队列进行优化
习题讲解
1. 最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数
第二行有
输出格式
一个整数表示答案。
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
提示
分别取出
代码参考
//B3637
#include<stdio.h>
#include<iostream>
using namespace std;
int a[5005], f[5005];
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
f[i] = 1;
}
for(int i = 2; i <= n; i++){
for(int j = 1; j <= i-1; j++){
if(a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
}
}
int ans = 0;
for(int i = 1; i <= n; i++){
ans = max(ans, f[i]);
}
cout << ans << endl;
return 0;
}
2. NASA的食物计划
题目背景
NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安全技术问题一直大伤脑筋,因此在各方压力下终止了航天飞机的历史,但是此类事情会不会在以后发生,谁也无法保证。所以,在遇到这类航天问题时,也许只能让航天员出仓维修。但是过多的维修会消耗航天员大量的能量,因此 NASA 便想设计一种食品方案,使体积和承重有限的条件下多装载一些高卡路里的食物。
题目描述
航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里。在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次。
输入格式
第一行
第二行
接下来
输出格式
一个数,表示所能达到的最大卡路里(int
范围内)
样例 #1
样例输入 #1
320 350
4
160 40 120
80 110 240
220 70 310
40 400 220
样例输出 #1
550
提示
对于
代码参考
#include <stdio.h>
#include <iostream>
using namespace std;
int v[55], m[55], kolo[55];
//dp数组
int f[55][405][405];
int main(){
int v_max, m_max;
int N;
int t1, t2;
cin >> v_max >> m_max;
cin >> N;
for(int i = 1; i <= N; i++)
cin >> v[i] >> m[i] >> kolo[i];
for(int i = 1; i <= N; i++){
for(int j = 0; j <= v_max; j++)
for(int k = 0; k <= m_max; k++){
t1 = v[i];
t2 = m[i];
f[i][j][k] = f[i-1][j][k];
if(j >= t1 && k >= t2)
f[i][j][k] = max(f[i-1][j][k], f[i-1][j-t1][k-t2]+kolo[i]);
}
}
cout << f[N][v_max][m_max];
return 0;
}
3. [NOIP2006 普及组] 开心的金明
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过
设第
请你帮助金明设计一个满足要求的购物单。
输入格式
第一行,为
从第
输出格式
样例 #1
样例输入 #1
1000 5
800 2
400 5
300 5
400 3
200 2
样例输出 #1
3900
提示
NOIP 2006 普及组 第二题
代码参考
//P2725
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
//f[i][j]数组代表当选到第i个物品,花了j元时,所能得到的价格与权重的乘积和最大值
int f[30][30005];
struct wp{
int v, p;
}w[30];
int main(){
int n, m;
cin >> n >> m;
for(int i = 1; i <= m; i++) cin >> w[i].v >> w[i].p;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
if(j >= w[i].v) f[i][j] = max(f[i-1][j], f[i-1][j-w[i].v] + w[i].v * w[i].p);
else f[i][j] = f[i-1][j];
}
}
int ans = 0;
for(int i = 1; i <= n; i++)
ans = max(ans, f[m][i]);
cout << ans << endl;
return 0;
}
4. [USACO3.1] 邮票 Stamps
题目描述
给一组
输入格式
输入的第一行是两个整数,分别代表邮票上限
自第二行起,除最后一行外,每行有
输出格式
输出一行一个整数代表
样例 #1
样例输入 #1
5 2
1 3
样例输出 #1
13
提示
样例输入输出 1 解释
有
。 。 。 。 。 。 。 。
然而,使用
数据规模与约定
对于
说明
题目翻译来自 NOCOW。
代码参考
//P2725
//完全背包
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int a[55];
//f[i]记录面值为i时最少需要的邮票数
int f[2000005];
int main(){
int n, k;
cin >> k >> n;
memset(f, 0x3f3f3f,sizeof(f));
f[0] = 0;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= 2000000; i++){
for(int j = 1; j <= n; j++){
if(i >= a[j]) f[i] = min(f[i-a[j]]+1, f[i]);
}
}
for(int i = 1; i <= 2000000; i++){
if(f[i] > k){
cout << i-1 << endl;
return 0;
}
}
return 0;
}
5.机器分配
题目描述
总公司拥有高效设备
输入格式
第一行有两个数,第一个数是分公司数
接下来是一个
输出格式
第一行为最大盈利值。
接下来
P.S. 要求答案的字典序最小。
样例 #1
样例输入 #1
3 3
30 40 50
20 30 50
20 25 30
样例输出 #1
70
1 1
2 1
3 1
代码参考
//P2066
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
ll a[15][20];
//f[i][j]代表取到第i家公司,用掉了j台设备时,能够获得的利益最大值
ll f[15][20];
//存答案
ll cnt[15][20][2];
ll cnt2[15];
int main(){
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
for(int k = 0; k <= j; k++){
if(f[i][j] <= f[i-1][j-k] + a[i][k]){
f[i][j] = f[i-1][j-k] + a[i][k];
cnt[i][j][0] = k;
cnt[i][j][1] = j-k;
}
}
}
}
//输出最大值
ll ans = 0, t = 0;
for(int i = 1; i <= m; i++){
if(ans <= f[n][i]){
t = i;
ans = f[n][i];
}
}
cout << ans << endl;
//输出每一项数量
for(int i = n; i >= 1; i--){
cnt2[i] = cnt[i][t][0];
t = cnt[i][t][1];
}
for(int i = 1; i <= n; i++) cout << i << " " << cnt2[i] << endl;
return 0;
}
本文作者:hsy2093
本文链接:https://www.cnblogs.com/hsy2093/p/18225375
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效