完全背包
状态表示
dp[i][j]:前 i 个物品,背包容量 j 下的最优解(最大价值)
完全背包和基本的d0 - 1背包不同的是,每一件物品可以无限拿,那么最基本的做法就是在0-1背包算法的基础上加一层物品数量的循环,但是这种做法会 TLE ,非常d疼。
题目
输入
4 5
1 2
2 4
3 4
4 5
输出
10
朴素代码(TLE)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int w[N],v[N];
int dp[N][N];//前 i 件物品,背包容量为 j 的最优解
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++)
cin >> w[i] >> v[i];
for(int i = 1;i <= n ;i ++)
{
for(int j = 0;j <= m ; j ++)
{
for(int k = 0; w[i] * k <= j;k ++)
{//枚举数量
dp[i][j] = max(dp[i][j],dp[i - 1][j - k * w[i]] + k * v[i]);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
其中的转移方程和0-1背包有点区别
dp[i][j] = max(dp[i][j],dp[i - 1][j - k * w[i]] + k * v[i]);
max的第一个参数为啥不是dp[i - 1][j]
呢?,因为当 k = 0的时候,就已经将上一状态转移到了当前状态
时间优化
朴素代码会TLE,所以我们需要想方法来优化一下代码
以这一组样例来说:
5 2
1 2
2 5
对于当前状态,我们不一定是将上一层的状态进行转移,而是将上一次的状态进行转移
就比如说当i = 2,j = 3的时候,很明显,当前状态是由当前层
dp[2][1]
转移而来的,即2 + 5 = 7
,并不是从上一层转移而来的,但是我们要对这两者进行比较。
即dp[i][j] = dp[i - 1][j]
。
转移方程为:
dp[i][j] = max(dp[i][j],dp[i][j - w[i]] + v[i])
是不是和01背包有点相似,01背包是将上一层的状态进行转移,而完全背包的话是将上一次状态进行转移
优化代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int w[N],v[N];
int dp[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++)
cin >> w[i] >> v[i];
for(int i = 1;i <= n ;i ++)
{
for(int j = 0;j <= m ; j ++)
{
dp[i][j] = dp[i - 1][j];
if(w[i] <= j)
dp[i][j] = max(dp[i][j],dp[i][j - w[i]] + v[i]);
}
}
cout << dp[n][m] << endl;
return 0;
}
空间优化
同样的,我们也可以对空间进行优化,对于01背包,当前状态是由上层状态的转移,而完全背包是对于这一层状态转移,完全背包的话,需要将上一状态进行转移到当前状态来模拟数量的枚举,不像01背包可以忽略数量。
优化代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int w[N],v[N];
int dp[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++)
cin >> w[i] >> v[i];
for(int i = 1;i <= n ;i ++)
{
for(int j = 0;j <= m ; j ++)
{
if(j >= w[i])
dp[j] = max(dp[j],dp[j - w[i]] + v[i]);
}
}
cout << dp[m] << endl;
return 0;
}