【第二十五课】动态规划:完全背包问题(acwing-3 / 公式推导 / 思路理解 / 优化 / c++代码)

目录

思路

朴素代码

优化

公式推导上 

二维代码 

一维代码

公式理解上


 

在开始看完全背包问题之前,可能需要先了解01背包及其解决办法。 指路👇

【第二十五课】动态规划:01背包问题(acwing-2 / 思路 / 含一维数组优化 / c++代码)

思路

这个问题和01背包的区别就是每件物品可以选无限次(当然在背包容量限制之内)

也就是说,01背包中我们在面对第i件物品的时候,有两种选择,分别是:选和不选,也就是第 i 件物品选择0次和1次,而完全背包也就是说,我们可选择的选项变多了,我们可以选择第i件物品 0,1,2,3,......k次,也就是在01背包分析问题方法的基础上,做了一点变化,如图

如果仅仅是这里发生了改变的话,其实我们在不考虑优化情况下,最朴素的写法应该是很容易得出的。

状态转移方程变为:dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*v[i]]+k*w[i]) 

都是为了填充这个二维数组,只不过把选择从0和1两个变成了0-k个

朴素代码

#include<iostream>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            for(int k=0;k*v[i]<=j;k++)
            {
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout<<dp[n][m];
    return 0;
}

这种写法应该可以理解。

但是我们也看到了嵌套了三层循环。因此我们想着对他进行优化

优化

公式推导上 

经过上面的分析我们知道完全与01背包问题的区别就是选择这件物品的次数,那么我们想试着用01背包的状态转移方程来写完全背包的,观察一下有没有什么规律,能够简化一层循环的。

下面图中是关于公式推导的过程 

下面这个视频老师讲了具体的公式推导的过程,也对二维数组的填充过程给出了呈现,非常详细,推荐看一下帮助理解二维的写法。(可以倍速观看)

 【叶老师讲信奥--完全背包问题】

下面这个老师是按照公式推导变化的方式进行讲解的,也很清晰,推荐看一下。

【完全背包问题求解-NOIP/CSP/蓝桥杯算法试学课-青少年C++编程】

二维代码 

#include<iostream>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            dp[i][j]=dp[i-1][j];
            if(j>=v[i])dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
        }
    }
    cout<<dp[n][m];
    return 0;
}

一维代码

#include<iostream>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int dp[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=v[i];j<=m;j++)
        {
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        }
    }
    cout<<dp[m];
    return 0;
}

公式理解上

如果不看公式推导,单纯考虑公式的理解方面来说,我们发现状态转移方程从

dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]) 这样,转变为

-

dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]) 这样

我们直观来看,其实后者也就是在第二个部分从i-1变成了i 。01背包问题我们是如何理解这个i-1的?也就是从第i行的前一行得出这一行的结果。那么这里变成了 i ,也就是说,我们本行的结果,就是通过本行的数据来得出的。为什么?就因为一件物品可以被选多次

我们想一下在01背包的一维数组优化里,我们说需要逆序遍历,是因为我们要用上一层的数据,如果正序的话,我们一件物品就会被选多次,得到的就是更改之后的数据。那么按照这个想法,运用到完全背包问题里,正好就是解决完全背包的方法。因此完全背包的一维优化我们可以直接这样理解。

我之前会有疑惑,正序遍历所造成的一件物品选择多次,这个多次是否涵盖了完全背包问题的所有可能次数?会不会有漏掉的情况呢?

答案是不会的,我们每一次正序遍历都会尝试在当前容量下选择当前物品,直到背包容量不足以再选择当前物品为止。因此,正序遍历实际上已经尝试了所有可能的选择次数,不会有漏掉的情况。

细品hh~


好啦,完全背包写到这里。

有问题欢迎指出,一起加油!!

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值