leetcode刷题:动态规划08(分割等和子集)

416. 分割等和子集

力扣题目链接

题目难易:中等

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

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

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

示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100
     看作 01背包问题
     数组和 是固定的 , 相当于 总容量 weight
            一共有n 个数
                     每个数只能被选择一次
                        一旦有的总和 和 为 1/2 sum  ,即结束
    总和为奇数直接返回,奇数哪来的平方
    以数字个数为横,所有的容量变化为纵,共 allSun/2+1个列
    dp[i][j]为当前位置的最大总和
    初始化时,第0列容量为0,初始化为0                    
    第一行初始化,如果当前位置能放下这个数,则记录,否则初始化为0
    第一列初始化为0,因为0谁也放不下
    如果第dp[i-1][j]为allsum/2,则找到目标值,返回
    如果nums[i]+dp[i-1][j]>result,就是当前值加上上一个值比需要总和大,则还用之前的。
    如果相等则找到,就返回
    如果小,则在前一个值和这个值之间取最大值
package com.programmercarl.dynamic;

/**
 * @ClassName CanPartition
 * @Descriotion TODO
 * @Author nitaotao
 * @Date 2022/7/27 0:36
 * @Version 1.0
 * https://leetcode.cn/problems/partition-equal-subset-sum/
 * 416. 分割等和子集
 **/
public class CanPartition {
    public boolean canPartition(int[] nums) {
        /**
         看作 01背包问题
         数组和 是固定的 , 相当于 总容量 weight
         一共有n 个数
         每个数只能被选择一次
         一旦有的总和 和 为 1/2 sum  ,即结束
         */
        int allSum = 0;
        for (int i = 0; i < nums.length; i++) {
            allSum += nums[i];
        }
        int n = nums.length;
        if (allSum % 2 == 1 || n == 1) {
            // 奇数哪来的平分,一个数哪来的平分
            return false;
        }
        // x轴长度
        int result = allSum / 2;
        //dp[i]为当前占用容量
        int[][] dp = new int[n][result + 1];
        //一共n个数,共需的总和(空间)为  [0,result]
        for (int i = 0; i < n; i++) {
            //第一列初始化为0,即sum还差0时
            dp[i][0] = 0;
        }
        for (int i = 1; i <= result; i++) {
            //第一行初始化
            if (i >= nums[0]) {
                dp[0][i] = nums[0];
            }
        }
        /**  0 1 2 3 4 5  总容量
         * 0 0 1 1 1 1 1
         * 1 0
         * 2 0
         * 3 0
         * 第
         * 几
         * 个
         */
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= result; j++) {
                if (dp[i - 1][j] == result) {
                    return true;
                }
                if (nums[i] + dp[i - 1][j] > result) {
                    //如果当前值放不进来
                    //当前值还用原来的
                    dp[i][j] = dp[i - 1][j];
                } else if (nums[i] + dp[i - 1][j] < result) {
                    //如果当前总和小于result,可以放置进来的
                    if (j >= nums[i]) {
                        //如果容量>这个数,才能放进来
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
                    } else {
                        //当前位置放不下则还用原来的
                        dp[i][j] = dp[i - 1][j];
                    }
                } else {
                    //如果遇到刚刚好,满了,就直接返回。
                    return true;
                }
                for (int k = 0; k < n; k++) {
                    for (int l = 0; l <= result; l++) {
                        System.out.print(dp[k][l] + "   ");
                    }
                    System.out.println();
                }

            }
        }
        return false;
    }

    public static void main(String[] args) {
//        System.out.println(new CanPartition().canPartition(new int[]{1, 2, 3, 4, 5, 6, 7}));
        System.out.println(new CanPartition().canPartition(new int[]{1, 1}));
//        System.out.println(new CanPartition().canPartition(new int[]{2, 2, 3, 5}));
//        System.out.println(new CanPartition().canPartition(new int[]{1, 5, 11, 5}));
    }
}

在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值