力扣第416题:动态规划解分割等和子集(Java)

一、前言 

        动态规划是算法中一个非常重要的部分,它可以代替回溯算法等解决问题,而且只要合理的得出状态转移方程,此类题目一般都可以较为简单的写出来,但是关键点就在于如何构造出状态转移方程。最近更新的这些题目都是我在力扣刷题是觉得较好的题目,希望和大家一起分享。

二、题目描述力扣第416题:动态规划解分割等和子集(Java)_第1张图片

 三、题目分析

        我觉得在明确知道这题可以用动态规划解决的话,首先应该思考如何构造出状态转移方程。首先我们的目的是将数组分为相等的两份,那么如果数组和为奇数,则直接返回false

int sum=0;
for(int i : nums)
    sum+=i;
if(sum%2!=0)
    return false;

        在sum为奇数之后,我们令target=sum/2,那么只要我们在nums数组中找到几个数的和等于target,则可以返回true了。那么我们的状态转移方程可以设为dp[ i] [ j],就是指前i个数之和为j。

可知i应该为nums.length+1(将第0个数省去了,dp[0][0]不考虑),j为target+1。最后返回的应该是dp[nums,length][target]==target,它的意思是,如果前i个数的和为target,则返回true,否则返回false。

        那么状态转移方程应该是什么呢?

        dp[i][j]=?,试想应该存在两种情况,因为dp[i][j]是从dp[i-1][j]中过来的,如果j>nums[i-1](前面说了0不算,所以此处i-1),那么这个第i个数选不选都可以,所以

dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-nums[i]+nums[i]);

反之,如果j

dp[i][j]=dp[i-1][j];

得到了状态转移方程,代码就很容易得出了

 public boolean canPartition(int[] nums) {
       if(nums.length<2)
        return false; 
       int sum=0;
       for(int i : nums)
            sum+=i;
        if(sum%2!=0)
        return false;
        int target=sum/2;
        int [][]dp=new int[nums.length+1][target+1];
        for(int i=1;i<=nums.length;i++)
        {
            for(int j=1;j<=target;j++)
            {
                if(j>=nums[i-1])
                    dp[i][j]= Math.max(dp[i - 1][j], dp[i-1][j - nums[i - 1]] + nums[i - 1]);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        return dp[nums.length][target]==target;
    }

力扣第416题:动态规划解分割等和子集(Java)_第2张图片

四、额外解法

        在写完之后,我也在评论区看到了一些其他的解法,例如DFS、位运算等等...,位运算相对难以理解,我就简单说一下DFS的大概思路。

        DFS(深度优先搜索)的方法大概是这样的,首先写一个dfs函数,里面传入一颗顺序二叉树(nums),target(跟上面一样,是数组和的一半),index(当前所在的树的层次)。

力扣第416题:动态规划解分割等和子集(Java)_第3张图片

                                                                (图非原创,侵删)

                第一层是空的,从第一层开始,左子树是数组的某个值,按照树的层数依次为1,2,3,5。右子树都是0。每次递归,如果遍历左子树,则递归中传入的target需要减去左子树上对应的值,index需要加一,如果遍历右子树则不需要对target做出改变,只需将index加一。直到!target=0,返回true结束,或者target<0或者index等于数组长度时返回false结束。

private boolean dfs(int[] nums, int target, int index) {
//targe等于0,说明存在一些元素的和等于sum/2,直接返回true
if (target == 0)
    return true;
//如果数组元素都找完了,或者target小于0,直接返回false
if (index == nums.length || target < 0)
    return false;
//选择当前元素和不选择当前元素两种情况
return dfs(nums, target - nums[index], index + 1)|| dfs(nums, target, index+1);

        这个方法代码量真的很少,也很方便,理解起来也容易..除了通不过,超时了。所以这种方法我也只是单纯提出来作为一种技巧吧。

        另外我立个flag,每日一题!!!

你可能感兴趣的:(动态规划,动态规划,算法)