动态规划:目标和

    问题一:给定一个正整数组nums和一个常数S,要求从nums中取出若干各数,使它们的和为S,问有几种取法。

    分析:按照0-1背包问题的方法

	public int getSum(int[] nums, int S) {
		int[][] dp = new int[nums.length + 1][S + 1];
		for(int i = 0; i <= nums.length; i++) { //得到0:一个元素都不取,有一种方法
			dp[i][0] = 1;
		}
		for(int i = 1; i <= nums.length; i++) {
			for(int j = 0; j <= S; j++) {
				if(j - nums[i - 1] >= 0)
					dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
				else
					dp[i][j] = dp[i - 1][j];
			}
		}
		return dp[nums.length][S];
	}

    因为本质上是一个累加的过程,因此上面的过程还可以进一步简化:dp[i]记录得到和为i的方法数,对nums中的元素,元素个数不断增加,而dp把可能的方法数累加即可。

	    public int getSum(int[] nums, int s) {
	        int[] dp = new int[s + 1]; 
	        dp[0] = 1;
	        for (int n : nums)
	            for (int i = s; i >= n; i--)
	                dp[i] += dp[i - n]; 
	        return dp[s];
	    } 

    问题二:给定一个非负整数数组nums和一个目标整数S,给nums中的数组分别赋予正号“+”和负号“-”,使得nums中的元素和等于S,求“+”和“-”的分配方式总共有多少种。如nums = [1, 1, 1, 1, 1],S = 3,则可能的分配方式共有5种:

    -1+1+1+1+1 = 3
    +1-1+1+1+1 = 3
    +1+1-1+1+1 = 3
    +1+1+1-1+1 = 3

    +1+1+1+1-1 = 3

    分析:题目实际上是要把nums分成两部分:正数部分和负数部分,设正数部分的和为P,负数部分的和为N(绝对值),nums的所有元素之和为sum。

    题目要求的目标为:P - N = S,而P + N = sum,两式相加,P = (S + sum) / 2,其中S和sum都是已知数。所以,题目变为以下问题:

    在nums中,寻找若干个元素,使得其和为P。

    public int findTargetSumWays(int[] nums, int s) {
        int sum = 0;
        for (int n : nums)
            sum += n;
        return sum < s || (s + sum) % 2 > 0 ? 0 : subsetSum(nums, (s + sum) >>> 1); 
    }   

    public int subsetSum(int[] nums, int s) {
        int[] dp = new int[s + 1]; 
        dp[0] = 1;
        for (int n : nums)
            for (int i = s; i >= n; i--)
                dp[i] += dp[i - n]; 
        return dp[s];
    } 

    

猜你喜欢

转载自blog.csdn.net/xiezongsheng1990/article/details/80070440