代码随想录训练营第37天|背包排列数

52. 携带研究材料

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

dp[i]:容量为i的背包,所能装载的最大价值

转移方程:尝试所有可能得物品i,占据空间weights[i],产生价值values[i]。遍历背包时使用正序遍历,意味着大容量的dp可以重复使用values[i],为完全背包问题。

518. 零钱兑换 II

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int n=coins.size();
        vector<int> dp(amount+1,0);
        dp[0]=1;
        for(auto& coin:coins){
            for(int i=coin; i<=amount; i++){
                dp[i]+=dp[i-coin];
            }
        }
        return dp[amount];

    }
};

 dp[i]:凑成金额i的不同方案数。

转移方程:因为coin可以任选,是完全背包问题,所以背包采用正序遍历。给定面额coin,如果使用它后凑到金额i,意味着使用之前已凑够i-coin,对应的方案数为dp[i-coin],累加所有可能得到dp[i]。

初始化dp[0]=1:

解释1:凑0元,即要求任何coin都不选,什么都不选也是1种方案。

解释2:假设要凑5元,恰好有面额5,按递推公式会累加dp[5-5],而直观上会新增1种方案,所以有:dp[5-5]=1。

377. 组合总和 Ⅳ

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1,0);
        dp[0]=1;
        for(int i=1; i<=target; i++){
            for(auto &num: nums){
                if(i-num>=0)
                    dp[i]+=dp[i-num];
            }
        }
        return dp[target];
    }
};

和上题的区别在于求的是排列数,解法也只是调换了一下内外层循环的顺序,原因:

1.上题的逻辑是每给定一个物品后,刷新滚动数组,更新方案数。由于物品遍历是有序的,在使用靠前的物品时,是无法利用靠后物品的信息的。举个例子,假设要凑够5元,有零钱1、4。在使用零钱1刷新滚动数组时,是不知4的存在的,此时统计的方案数也绝对不会出现(1,4),只有当遍历到零钱4时,才会将(4,1)的情况计入。

2.本题的逻辑是先给定一个背包容量,然后尝试所有可能的物品,更新方案数。这里差异就出来了,还是沿用1中的例子,dp[5]的计算中:

选定1元,累加dp[4],dp[4]的计算会考虑全部元素,因此(1,4)会被计入。

选定4元,累加dp[1],(4,1)会被再次计入。

因此,这种遍历顺序统计的其实是排列数。

57. 爬楼梯

#include<iostream>
#include<vector>
using namespace std;

int main(){
    int n,m;
    cin>>n>>m;
    vector<int> dp(n+1,0);
    dp[0]=1;
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(i-j>=0)
                dp[i]+=dp[i-j];
        }
        // cout<<i<<":"<<dp[i]<<endl;
    }
    cout<<dp[n];
    return 0;
}

dp[i]:移动到第i层的方案数。

转移方程:假设是移动j(按题意1<=j<=m)步后才到达第i层,那么意味着移动前应该处于(i-j)层,对应的方案数为dp[i-j],累加所有情况得到dp[i]。

因为考虑顺序,所以求的是排列数,先遍历背包,再遍历物品。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值