377. Combination Sum IV

这个题的思路是动态规划,DP[n]代表能组成n的组合数。算1-n就可以,因为都是正数。

算dp[n]的时候遍历num[1] to num[n] index = i

如果i < dp[n] 那么dp[n] = dp[n] + dp[n-i]

从逻辑上来考虑比较复杂,比如4=0+4 1+3 2+2 3+1 4+0

0+4 1 (1+1+1+1)
1+3 1的组合3的组合
2+2 2的组合
2的组合
3+1 3的组合*1的组合
4+0 1 (4)

1+4+4+4+1 然后除以2 因为重复了一遍
但是结果似乎不对

看答案 算法是

dp[n] = dp[n] + dp[n-i]
比如4
从1遍历到4
1<4
dp[4] = dp[4] + dp[4-1]

public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        dp[0] = 1;
        
        for(int n = 1; n < target+1;n++)
        {
            for(int m = 0; m < nums.length;m++)
            {
                if(n >= nums[m]) dp[n] = dp[n] + dp[n-nums[m]];
            }
        }
        
        return dp[target];
    }

代码很简单,但是发现这种逻辑很难,比如我就觉得无从下手。。

感觉DP的题重点是找到动态规划的递推式,已经习惯于DP[N]从dp[n-1]来找

这个题就不是。。
递推式dp[n] = dp[n] + dp[n-i]


二刷:

一开始先排序,然后因为1,3和3,1这种都算有效,所以滑动窗口之类的没有意义。
而且,还有错,不能有效break。

看一刷的答案,发现是动态规划。

当时不理解,现在理解了。


public int combinationSum4(int[] nums, int target) 
    {
        Arrays.sort(nums);
        int[] dp = new int[target+1];
        dp[0] = 1;
        
        for(int i = 1; i <=target;i++)
        {
            for(int j = 0; j < nums.length; j++)
            {
                if(i - nums[j]>=0) dp[i] = dp[i] + dp[i - nums[j]];
                else break;
            }
        }
        
        return dp[target];
    }

然后改进了一下,先排序,可以有效break,比直接遍历快一点点,而且好理解。

理解容易,不一定能想出来。
一开始有那个点意思,想到的是方法是递归。比如(nums[],target = 4
递归就变成(nums[],target-nums[n])
仔细想想应该就是动态规划的思路,然后通过DP来减少重复计算。

DP的双For loop大概可以总结为 i = 1 ~ n; j = 0 ~ i,从左边开始的话。

右边开始是 i = n~0; j = n~n-i???




三刷。

照着刚才的惯性DFS结果TLE了,做的时候就觉得不对……

应该是DP。。

dp[i]代表:
和是i的组合有多少种 的dp[4] = 1 + dp[3]

4 = 1 + 3
4 = 2 + 2
4 = 3 + 1

A + B的时候前面的A是nums里的数, 后者是可以组成B的左右组合,不能是dp[1] + dp[3]这样的话就重复计算了.
dp[1] + dp[2] 和 dp[2] + dp[1]计算了2次1+1+1...,这就是为啥一刷的办法不对。

Time: O(nk)
Space: O(k)

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        if (nums.length == 0) return 0;
        Arrays.sort(nums);
        int[] dp = new int[target+1];
        dp[0] = 1;
        
        for (int i = 1; i <= target; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i - nums[j] >= 0) dp[i] += dp[i - nums[j]];
                else break;
            }
        }
        return dp[target];
    }
}
原文地址:https://www.cnblogs.com/reboot329/p/5875877.html