分割等和子集 JAVA

日期:2020-10-11

作者:19届 LZ

标签:JAVA NP完全问题

题目描述

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

注意:

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

示例:

示例 1:

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

输出: true

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

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

输出: false

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

解题思路:

存在几种情况可以直接舍弃:
1,数组大小为1或0;
2,数组和为奇数
3,数组存在某个数大于数组和的一半

设立一个数组dp[i][j],表示是否可以从nunms[0]到nums[i]中选取元素使其和为j;

则以下条件恒成立
dp[i][0]=true;
dp[0][nums[0]]=true;

状态转移方程:
当i>0,j>0时:
如果nums[i]<=j,则dp[i][j]可以由dp[i-1][j-nums[i]]或者dp[i-1][j]得到
如果nums[i]>j,则dp[i][j]只能由dp[i-1][j]得到

代码

class Solution {
    public boolean canPartition(int[] nums) {
        int n=nums.length;

        if(n<2){//舍弃数组大小为1或0的情况
          return false;
        }

        int all=0,max=0;

        for(int num:nums){
            all+=num;
            max=Math.max(num,max);
        }

        if(all%2==1){//和为奇数,舍去
           return false;
           }

        int target=all/2;
             if(max>target)//舍弃存在一个数大于和的一半的情况
                return false;
        
        boolean[][] dp = new boolean[n][target + 1];
                   //dp[i][j]表示是否可以从nunms[0]到nums[i]中选取元素和为j


        for(int i=0;i<n;i++){
            dp[i][0]=true;//若不选取,和为0恒成立
        }
        dp[0][nums[0]]=true;//只能选择nums[0],所以恒成立

        for(int i=1;i<n;i++){   //状态转移方程
            for(int j=1;j<target+1;j++){
                if(nums[i]<=j){
                    dp[i][j]=dp[i-1][j-nums[i]]|dp[i-1][j];
                }
                else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
    
    return dp[n-1][target];
    }
}

猜你喜欢

转载自blog.csdn.net/cyl_csdn_1/article/details/109013460
今日推荐