当前位置:   article > 正文

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

力扣416

一、前言 

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

二、题目描述

 三、题目分析

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

  1. int sum=0;
  2. for(int i : nums)
  3. sum+=i;
  4. if(sum%2!=0)
  5. 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<nums[i-1],那么这个第i个数一定不可以选 ,因为一旦选了,所选部分的和加起来就大于j了,那么最后的结果也就大于target,所以

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

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

  1. public boolean canPartition(int[] nums) {
  2. if(nums.length<2)
  3. return false;
  4. int sum=0;
  5. for(int i : nums)
  6. sum+=i;
  7. if(sum%2!=0)
  8. return false;
  9. int target=sum/2;
  10. int [][]dp=new int[nums.length+1][target+1];
  11. for(int i=1;i<=nums.length;i++)
  12. {
  13. for(int j=1;j<=target;j++)
  14. {
  15. if(j>=nums[i-1])
  16. dp[i][j]= Math.max(dp[i - 1][j], dp[i-1][j - nums[i - 1]] + nums[i - 1]);
  17. else
  18. dp[i][j]=dp[i-1][j];
  19. }
  20. }
  21. return dp[nums.length][target]==target;
  22. }

四、额外解法

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

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

                                                                (图非原创,侵删)

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

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/744901
推荐阅读
相关标签
  

闽ICP备14008679号