• [LeetCode]494. 目标和、416. 分割等和子集(0-1背包,DP)


    题目一 494. 目标和

    给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

    返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

    示例 1:

    输入: 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

    一共有5种方法让最终目标和为3。
    注意:

    数组非空,且长度不会超过20。
    初始的数组的和不会超过1000。
    保证返回的最终结果能被32位整数存下。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/target-sum
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    题解

    由题,可以将数字分为两个集合(子集和余集)。
    (有) 子集sum-余集sum=S
    (两边同时加上子集sum+余集sum)子集sum+余集sum+子集sum-余集sum=子集sum+余集sum+S
    (即) 2*子集sum=集合sum+S
    (即) 子集sum=(集合sum+S)/2
    故问题转换为求子集和为固定值有多少种组合情况其中每个数都可以选或不选,可作0-1背包问题求解。
    在遍历到num时,dp[j]表示只使用num及之前遍历过的num,和为j的组合情况种数。 ,可以和经典0-1背包问题的一维数组情况比对,理解0-1背包问题。

    其他

    todo 还可以用dfs求解。

    代码

    class Solution {
        public int findTargetSumWays(int[] nums, int S) {
            int setSum=0;
        	for(int num:nums) {
            	setSum+=num;
            }
        	
        	if((setSum+S)%2==1) {
        		return 0;
        	}
        	
        	int subSetSum=(setSum+S)/2;
        	int[] dp=new int[subSetSum+1];
        	dp[0]=1;
        	for(int num:nums) {
        		for(int j=subSetSum;j>=num;--j) {
        			dp[j]+=dp[j-num];
        		}
        	}
        	return dp[subSetSum];
        }
    }
    

    题目二 416. 分割等和子集

    给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

    注意:

    每个数组中的元素不会超过 100
    数组的大小不会超过 200
    示例 1:

    输入: [1, 5, 11, 5]

    输出: true

    解释: 数组可以分割成 [1, 5, 5] 和 [11].

    示例 2:

    输入: [1, 2, 3, 5]

    输出: false

    解释: 数组不能分割成两个元素和相等的子集.

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/partition-equal-subset-sum
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    题解

    • 看到这个题,就联想到了上面的题。同样可变为求子集和是否成立的问题,即可以用0-1背包求解。
    • 要注意和为奇数的特判

    todo

    • 事实上这个题在第70+个用例没过,但是平台上测试这个用例是过的,改天再提一下试试。
    • 还有进阶的分为k个和相等的子集,待做。

    代码

    public static boolean canPartition(int[] nums) {
    		if (nums == null || nums.length == 0) {
    			return false;
    		}
    
    		int sum = 0;
    		for (int num : nums) {
    			sum += num;
    		}
    		if (sum % 2 == 1) {
    			return false;
    		}
    
    		int half = sum / 2;
    		int[] dp = new int[half + 1];
    		Arrays.fill(dp, 0);
    		dp[0] = 1;
    		for (int num : nums) {
    			for (int j = half; j >= num; --j) {
    				dp[j] += dp[j - num];
    			}
    			if (dp[half] > 0) {
    				return true;
    			}
    		}
    		return false;
    	}
    
  • 相关阅读:
    手机操作
    模拟手机操作
    get_attribute_value
    test_order
    信息收集-FOFA资产收集与FOFA api
    html中form讲解
    安装redis
    yum vs rpm
    yum 安装java环境
    显示当前目录所有文件大小的命令ls -lht
  • 原文地址:https://www.cnblogs.com/coding-gaga/p/11716714.html
Copyright © 2020-2023  润新知