蓝桥杯2022(十三届)国赛——2022(背包)

2022(背包)

2022 - 蓝桥云课 (lanqiao.cn)

非常的熟悉,但是同样也仅仅是熟悉罢了…回头把背包好好复习一下。

三维:

#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

可见,关键是对当前选取物品个数的反向,也就是对附加条件的反向。至于原理,小弟还不太清楚,只能暂且记住这个结论——若是简化第一维反向遍历附加条件的循环。若是我对这个的理解有误,请各位大大指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值