01背包
01背包问题,指一堆(N个)有重量(weight)和价值(value)的物品,每种有一个,有一个特定容量 ( j ) 的背包,如何装到最大价值
我们用dp[N][j]表示在第N个物品,背包还剩J时的最大价值
则状态转移方程:dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i])
其中dp[i-1][j]代表不装第i个物品
dp[i-1][j-weight[i]] + value[i]代表装第i个物品
则可得出:
void back()
{
for(int i = 1;i <= N;++i)
{
for(int j = 0;j <= 999;++j)
{
if(j >= weight[i]) dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i]);
else dp[i][j] = dp[i-1][j];
}
}
}
这样的函数即可计算所有的dp值
可以进行一些优化
#include <bits/stdc++.h>
using namespace std;
#include <bits/stdc++.h>
using namespace std;
int main()
{
int dp[11][51] = {};
int weight[11] = {};
int value[11] = {};
for(int i = 1;i < 11;++i) cin >> weight[i] >> value[i];
for(int i = 1;i < 11;++i)
{
for(int j = 0;j <= 50 ;++j)
{
if(j >= weight[i]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
else dp[i][j] = dp[i-1][j];
}
}
cout << dp[10][50];
return 0;
}
因为在循环过程中会有i-1,所以要从0开始存储
现在降维,将dp转换为一维,这样就可以从0开始了,即dp[j] = max(dp[j],dp[j-weight[i]])
在进行一些优化得出:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int dp[51] = {};
int weight[10] = {};
int value[10] = {};
for(int i = 0;i < 10;++i) cin >> weight[i] >> value[i];
for(int i = 0;i < 10;++i)
{
for(int j = 50;j >= weight[i];--j)
{
dp[j] = max(dp[j],dp[j-weight[i]] + value[i]);
}
}
cout << dp[50];
}
/*
10 50
20 100
30 120
5 15
15 40
25 70
40 180
12 35
8 20
35 105
*/
答案是:230
完全背包
根0-1背包不同的是:物品可以放入多次
先给代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int dp[51] = {};
int weight[10] = {};
int value[10] = {};
for(int i = 0;i < 10;++i) cin >> weight[i] >> value[i];
for(int i = 0;i < 10;++i)
{
for(int j = weight[i];j < 51;++j)
{
dp[j] = max(dp[j],dp[j-weight[i]] + value[i]);
}
}
cout << dp[50];
}
/*
10 50
20 100
30 120
5 15
15 40
25 70
40 180
12 35
8 20
35 105
*/
答案是:250
可以看出与0-1背包只差了一个循环的顺序
为什么变换以下顺序就可以解决完全背包问题了,而二维时无论顺序如何都是0-1背包?
从小到大遍历时:当遍历到物品x时,如果dp[ j ]加入了 x,等到 j 遍历到 2*j 时,
dp[2*j] == dp[j] + value[j],所以会再次添加,而从大到小则不会这样
从大到小遍历时:dp[2*j] == dp[j] + value[j]由于dp[j]还没有被计算过,为0,也就是说,物品不会重复添加。
而对于二维来说:因为有 i 的维度存在,当遍历到 2*j 时,dp[x][2*j] != dp[x-1][j] + value[j],防止了这种情况的出现
多重背包
也是0-1背包的变体,根其他背包不同的是,一个物品不在有一个或无限个,而是Ki个,即每个物品有固定的个数
这种问题的解决方法是将多重背包转换为0-1背包,在数组种多次加入物品即可;
或者,在循环种加入一层数量的循环即可
#include <bits/stdc++.h>
using namespace std;
int main()
{
int dp[51] = {};
int weight[10] = {};
int value[10] = {};
int nums[10] = {};
for(int i = 0;i < 10;++i) cin >> weight[i] >> value[i] >> nums[i];
for(int i = 0;i < 10;++i)
{
for(int j = 50;j >= weight[i];--j)
{
for(int k = 1;k <= nums[i] && k*weight[i] <= j;++k)
dp[j] = max(dp[j],dp[j-k*weight[i]] + k*value[i]);
}
}
cout << dp[50];
}
/*
10 1000 2
20 100 1
30 120 1
5 15 1
15 40 1
25 70 1
40 180 1
12 35 1
8 20 1
35 105 1
*/