题解 P1616 【疯狂的采药】
本题解主要详细讲述了完全背包算法的原理
这题是一个标准的完全背包问题,在分析的时候我们就把题目中的背景去掉,体积就是时间,这样更有利于分析此题以及联想其他背包题目
不难得出递推式:
————这里已经进行了数组压维,这样可以优化空间
我们先来假设一下输入数据是这样的:
4 5
1 2
2 4
3 4
4 5
不难看出输出应该是
但是究竟是怎样得出这个结果的呢?
下面展示一下原理:(
首先dp数组初始化全为0:给定物品种类有4种,包最大体积为5,数据来源于题目的输入
v[1] = 1, w[1] = 2
v[2] = 2, w[2] = 4
v[3] = 3, w[3] = 4
v[4] = 4, w[4] = 5
i = 1 时: j从v[1]到5
dp[1] = max(dp[1],dp[0]+w[1]) = w[1] = 2 (用了一件物品1)
dp[2] = max(dp[2],dp[1]+w[1]) = w[1] + w[1] = 4(用了两件物品1)
dp[3] = max(dp[3],dp[2]+w[1]) = w[1] + w[1] + w[1] = 6(用了三件物品1)
dp[4] = max(dp[4],dp[3]+w[1]) = w[1] + w[1] + w[1] + w[1] = 8(用了四件物品1)
dp[5] = max(dp[3],dp[2]+w[1]) = w[1] + w[1] + w[1] + w[1] + w[1] = 10(用了五件物品)
i = 2 时:j从v[2]到5
dp[2] = max(dp[2],dp[0]+w[2]) = w[1] + w[1] = w[2] = 4(用了两件物品1或者一件物品2)
dp[3] = max(dp[3],dp[1]+w[2]) = 3 * w[1] = w[1] + w[2] = 6(用了三件物品1,或者一件物品1和一件物品2)
dp[4] = max(dp[4],dp[2]+w[2]) = 4 * w[1] = dp[2] + w[2] = 8(用了四件物品1或者,两件物品1和一件物品2或两件物品2)
dp[5] = max(dp[5],dp[3]+w[2]) = 5 * w[1] = dp[3] + w[2] = 10(用了五件物品1或者,三件物品1和一件物品2或一件物品1和两件物品2)
i = 3时:j从v[3]到5
dp[3] = max(dp[3],dp[0]+w[3]) = dp[3] = 6 # 保持第二轮的状态
dp[4] = max(dp[4],dp[1]+w[3]) = dp[4] = 8 # 保持第二轮的状态
dp[5] = max(dp[5],dp[2]+w[3]) = dp[4] = 10 # 保持第二轮的状态
i = 4时:j从v[4]到5
dp[4] = max(dp[4],dp[0]+w[4]) = dp[4] = 10 # 保持第三轮的状态
dp[5] = max(dp[5],dp[1]+w[4]) = dp[5] = 10 # 保持第三轮的状态
上面模拟了完全背包的全部过程,也可以看出,最后一轮的dp[m]即为最终的返回结果。
这便是完全背包的基本原理,应用到本题也是一样
那么这个算法的时间复杂度就是
注意:本题数据已更新,需要开
下面附上AC代码:
//这里代码中t数组就是上面讲解的v数组,v数组是上面的w数组
//因为我写代码的时候按背景中的时间(time)和价值(value) 写的
#include<iostream>
#include<algorithm>
using namespace std;
const int maxm = 10010, maxt = 10000010;
long long v[maxm], t[maxm], f[maxt];//开longlong!
int main(){
int T , m;
cin >> T >> m;
for(int i = 1;i <= m ;i ++) cin >> t[i] >> v[i];
for(int i = 1;i <= m;i ++){
for(int j = t[i];j <= T;j ++){
f[j] = max(f[j],f[j - t[i]] + v[i]);
}
}
cout << f[T];
}
感觉讲的挺详细的,完结撒花!