2022(背包)
非常的熟悉,但是同样也仅仅是熟悉罢了…回头把背包好好复习一下。
三维:
#include<iostream>
using namespace std;
long long int f[2023][11][2023]; //表示前2022个物品选择10个物品,体积总和为2022的方案个数 ,,数组下标为1开始,所以使2023,11,2023
int i, j, k;
int main()
{
for (i = 0; i <= 2022; i++) //因为体积为0,物品为0的所有情况都只有一种选择,即什么都不选
f[i][0][0] = 1;
for (i= 1; i <= 2022; i++) //枚举所有物品
{
for (j = 1; j <= 10; j++) //选了几个物品
{
for (k = 1; k <= 2022; k++) //枚举体积
{
f[i][j][k] = f[i-1][j][k]; //不选第i个物品
if (k >= i) //选第i个物品
f[i][j][k] += f[i-1][j - 1][k - i];
}
}
}
cout << f[2022][10][2022];
return 0;
}
二维:
下面这种算法还是比较符合学习机理的。首先是递推公式,因为求的是方法数量,所以用 dp[j]+=dp[j-w[i]]
,题目只不过多了一维,变式了而已。其次,因为我们省略了第一维,所以需要将条件从后往前遍历,这是01背包经典套路。值得注意的是,我们应该反向遍历哪个条件呢?
#include <iostream>
using namespace std;
const int MAXX = 2022 + 7;
long long dp[17][MAXX];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
dp[0][0] = 1;
for (int i = 1; i <= 2022; i++)
for (int j = 10; j >= 1; j--) //反向遍历 选取个数
for (int k = i; k <= 2022; k++)
dp[j][k] += dp[j - 1][k - i];
cout << dp[10][2022];
return 0;
}
反向遍历
只反向当前选取的物品个数——最终答案正确
for(int i=1;i<=2022;i++) //遍历物品
for(int j=10;j>=1;j--) //当前选取的物品个数
for(int k=2022;k>=i;k--) //背包容量
{
dp[j][k]+=dp[j-1][k-i];
}
// 379187662194355221
只反向背包容量(k)——结果错误
for(int i=1;i<=2022;i++) //遍历物品
for(int j=1;j<=10;j++) //当前选取的物品个数
for(int k=2022;k>=i;k--) //背包容量
{
dp[j][k]+=dp[j-1][k-i];
}
// 463518126748992849
两者都反向——可以获得正确结果
for(int i=1;i<=2022;i++) //遍历物品
for(int j=10;j>=1;j--) //当前选取的物品个数
for(int k=2022;k>=i;k--) //背包容量
{
dp[j][k]+=dp[j-1][k-i];
}
// 379187662194355221
可见,关键是对当前选取物品个数的反向,也就是对附加条件的反向。至于原理,小弟还不太清楚,只能暂且记住这个结论——若是简化第一维反向遍历附加条件的循环。若是我对这个的理解有误,请各位大大指正!