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

一、前言 

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

二、题目描述

 三、题目分析

        我觉得在明确知道这题可以用动态规划解决的话,首先应该思考如何构造出状态转移方程。首先我们的目的是将数组分为相等的两份,那么如果数组和为奇数,则直接返回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<nums[i-1],那么这个第i个数一定不可以选 ,因为一旦选了,所选部分的和加起来就大于j了,那么最后的结果也就大于target,所以

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;
    }

四、额外解法

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

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

                                                                (图非原创,侵删)

                第一层是空的,从第一层开始,左子树是数组的某个值,按照树的层数依次为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,每日一题!!!

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
目描述: 给你两个版本号 version1 和 version2 ,请你比较它们。 版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由多位数字组成,可能包含前导零。每个版本号至少包含一个字符。修订号从左到右编号,下标从0开始,最左边的修订号下标为0 ,下一个修订号下标为1,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。 比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较忽略任何前导零后的整数值。也就是说,修订号1和修订号001相等。如果版本号没有指定某个下标处的修订号,则该修订号视为0。例如,版本1.0 小于版本1.1,因为它们下标为0的修订号相同,而下标为1的修订号分别为0和1,0 < 1。 返回规则如下: 如果 version1 > version2 返回 1, 如果 version1 < version2 返回 -1, 否则返回 0。 示例 1: 输入:version1 = "1.01", version2 = "1.001" 输出:0 释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1" 示例 2: 输入:version1 = "1.0", version2 = "1.0.0" 输出:0 释:version1 没有指定下标为 2 的修订号,即视为 "0" 示例 3: 输入:version1 = "0.1", version2 = "1.1" 输出:-1 释:version1 中下标为 0 的修订号是 0,version2 中下标为 0 的修订号是 1 。0 < 1,所以 version1 < version2 示例 4: 输入:version1 = "1.0.1", version2 = "1" 输出:1 示例 5: 输入:version1 = "7.5.2.4", version2 = "7.5.3" 输出:-1 提示: 1 <= version1.length, version2.length <= 500 version1 和 version2 仅包含数字和 '.' version1 和 version2 都是 有效版本号

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少๑渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值