动态规划:背包问题

01背包问题

有N件物品和最多可以装W的背包,第i件物品质量为weight[i],价值为value[i]。每个物品只用一次,求怎么装才能获得最大价值。

二维dp数组:

dp数组及下标含义dp[i][j]表示从下标为[0-i]的物品里任意取放进容量为j的背包,价值总和的最大值。

递推公式dp[i][j]有两种方式推出来

  • 不放物品i:由dp[i-1][j]推出,即背包容量为j,只在[0-(i-1)]中取的最大价值总和。
  • 放物品i:由dp[i-1][j-weight[i]]+value[i],即考虑不放物品i,然后只在[0-(i-1)]中选择,然后再将物品i放回去。
  • 所以递推公式为:dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])

dp数组的初始化

dp[i][0]=0

对于dp[0][j]:如果j<weight[0]时,还不够放入第0件,所以dp[0][j]=0

​ 如果j>=weight[0],则刚好放入第0件,所以dp[0][j]=value[0]

完整代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {    
    vector<int> weight = {1,3,4};    
    vector<int> value = {15,20,30};    
    int all = 4;    
    vector<vector<int>> dp(weight.size(), vector<int>(all + 1,0));    
    for (int j = weight[0]; j < all; j++) dp[0][j] = value[0]; //初始化    
    for (int i = 0; i < weight.size(); i++) {        
        for (int j = 0; j <= all; j++) {            
            if (j < weight[i]) dp[i][j] = dp[i - 1][j];            
            else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);        
        }    
    }    
    cout<<dp[weight.size() - 1][all];
}

方法二:滚动数组

使用二维dp时,递推公式为:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])从递推公式可以看出,第i行只与第i-1行有关,最后我们也只要最后一个的数值,所以可以把第i层的数据拷贝到第i层,则递推公式可以变为dp[i][j]=max(dp[i][j],dp[i][j-weight[i]]+value[i])。这样可以省去变量i,就只用一个一维数组了。

但是计算j时要用到j-weight[i],所以每一层需要从后开始遍历。

如果从前往后遍历,则会造成j考虑了两次物品i,因为在更新j-weiht[i]时已经考虑了物品i,在更新j时要使用j-weight[i]同时又考虑了一次物品i,所以相当于考虑了两次物品i

dp数组及下标含义

dp[i]表示容量为i时可以装的最大价值总和。

递推公式

dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

dp数组初始化

if (j < weight[0]) dp[j] = 0;else dp[j] = value[0];

遍历顺序

for (1:weight.size()) //外层循环遍历物品 for (all:weight[i])  //内层循环遍历背包容量到weight[i]

完整代码

#include<iostream>
#include<vector>
#include<algorithm>using namespace std;
int main() {    
    vector<int> weight = {1,3,4};    
    vector<int> value = {15,20,30};    
    int all = 4;    
    vector<int> dp(all + 1, 0);    
    for (int j = weight[0]; j <= all; j++) dp[j] = value[0];    
    for (int i = 0; i < weight.size(); i++) {        
        for (int j = all; j >= weight[i]; j--) {            
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);        
        }    
    }    
    cout<<dp[all];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值