组合总和Ⅳ数学推导(看到大佬思路做一下记录)

1、解决方法
本题和“将背包装满有多少种方法”中组合问题是有所不同的。

目标和的排列问题:排列问题与普通的组合问题不同,需要提前遍历不同排列为后面的dp提供数据,为什么?个人认为本题和背包问题毫无关联,硬套背包模板只会走入误区,理解二维dp数组的定义和转移方程的推导最为重要,一维的dp方案也是由二维优化而来,但本题解在此尝试给出尽可能详细的数学证明。

2、数学推导
设n为nums的总数。
设dp[i][j]为:选取了前i个数(nums[0…i−1])分别作为排列的最后一个元素(前面的元素没有限制),且排列之和为j的所有排列的总数,根据问题,我们需要得到和为target的所有排列,即dp[n][target]。

添加辅助函数:设P[i][j]为:选取第i个数(nums[i−1])作为排列的最后一个元素,且排列之和为j的所有排列的总数。显然,不同的P[i][j]所记录的排列之间互不重叠,则有:
d p [ i ] [ j ] = ∑ k = 0 i P [ k ] [ j ] dp[i][j]=\sum_{k=0}^iP[k][j] dp[i][j]=k=0iP[k][j]
提出最后一项:
d p [ i ] [ j ] = ∑ k = 0 i − 1 P [ k ] [ j ] + P [ i ] [ j ] = d p [ i − 1 ] [ j ] + P [ i ] [ j ] dp[i][j]=\sum_{k=0}^{i-1}P[k][j]+P[i][j]=dp[i-1][j]+P[i][j] dp[i][j]=k=0i1P[k][j]+P[i][j]=dp[i1][j]+P[i][j]

那么,只需要知道P[i][j],就可得到完整的转移方程。

考虑P[i][j]内的所有排列,由于其末尾元素都是nums[i−1],前面的元素没有限制(即nums内可选的所有元素),那么我们把这些排列的末尾元素全部移除,就构成了一组新的排列,即:总和为j−nums[i−1]的所有排列(没有限制),根据dp的定义,我们得到了P[i][j]=dp[n][j−nums[i−1]],因此,完整的转移方程为:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ n ] [ j − n u m s [ i − 1 ] ] dp[i][j]=dp[i-1][j]+dp[n][j-nums[i-1]] dp[i][j]=dp[i1][j]+dp[n][jnums[i1]]

可以自行画图感受一下每次dp计算所必须的状态,自然能够推导出遍历方向:外层遍历target(行),内层遍历nums(列)
一个简单的具体用例:nums = {1,2,3}, target = 7

请添加图片描述

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        using ull = unsigned long long ;
        int n = nums.size();

        vector<vector<int>> dp(n + 1, vector<int>(target + 1, 0));
        dp[0][0] = 1;

        for(int j = 0; j <= target; j++){
            for(int i = 1; i <= n; i++){
                //                    remove this part: use ull
                if(j < nums[i - 1] || dp[i-1][j] > INT32_MAX - dp[n][j - nums[i - 1]]) 
                    dp[i][j] = dp[i-1][j];
                else dp[i][j] = dp[i-1][j] + dp[n][j - nums[i - 1]];
            }
        }

        return dp[n][target];
    }
};

作者:Sterina
链接:https://leetcode.cn/problems/combination-sum-iv/solutions/2663854/zu-he-zong-he-ivshu-xue-tui-dao-xian-gou-ap8y/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

空间优化:由于计算时,我们只使用该列上一行(dp[i-1][j])和前面某列最末尾元素(dp[n][j - nums[i - 1]),因此可将dp空间优化为一维。

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        using ull = unsigned long long ;

        vector<ull> dp1D(target + 1, 0);
        dp1D[0] = 1;
        for(int t = 0; t <= target; t++){
            for(int num : nums){
                if(t >= num) dp1D[t] += dp1D[t - num];
            }
        }

        return dp1D[target];
    }
};

作者:Sterina
链接:https://leetcode.cn/problems/combination-sum-iv/solutions/2663854/zu-he-zong-he-ivshu-xue-tui-dao-xian-gou-ap8y/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值