动态规划-将数组分成两个数组,并最小化数组和的差II(java)

265 篇文章 2 订阅
235 篇文章 0 订阅

将数组分成两个数组,并最小化数组和的差II

给定一个正数数组arr,请把arr中所有的数分成两个集合 如果arr长度为偶数,两个集合包含数的个数要一样多 如果arr长度为奇数,两个集合包含数的个数必须只差一个 请尽量让两个集合的累加和接近 返回最接近的情况下,较小集合的累加和.

暴力递归

解题思路

先计算数组的累加和sum,我们递归时让选出来的数字累加和逼近于sum/2,
这样拆分出的两个数组累计和就是最接近的,此时还有一个选择个数的限制,
因此递归时,除了考虑每个位置上的数字选不选,还要考虑选择的个数,因此递归时,我们要三个变量,代码里看把,

代码演示

    /**
     * 递归拆分数组
     * @param arr
     * @return
     */
    public static int right(int[] arr) {
        if (arr == null || arr.length < 2){
            return 0;
        }
        int sum  = 0;
        for (int a : arr){
            sum += a;
        }
        //长度是偶数时,只有一个选择
        if ((arr.length & 1) == 0){
            return process(arr,0,sum / 2,arr.length / 2);
        }else{
            //长度为奇数时,讨论两种情况
            return Math.max(process(arr,0,sum / 2,arr.length / 2),process(arr,0,sum / 2,arr.length / 2 + 1));
        }

    }

    /**
     * 递归
     * @param arr 数组
     * @param index 下标
     * @param rest 累加和的目标值
     * @param size 需要选择的元素个数
     * @return
     */
    public static int process(int[] arr,int index,int rest,int size){
        //base case 越界时,没有可选择的数了
        if (index == arr.length){
            //如果到最后一个位置,需要选择的元素,刚好选完,就没有可选了,返回0.
            // size 不等于0,之前的选择是无效的,返回-1 标记
            return size == 0 ? 0 : -1;
        }
        //当前元素不选择时,index + 1 ,去下一个位置选,
        int p1 = process(arr,index + 1,rest,size);
        //无效值时我们用-1 标记,因此p2 也要用-1 初始化,避免比较结果时产生影响
        int p2 = -1;
        if (arr[index] <= rest){
            //选择时,去下一个位置继续选择index + 1
            //累加和减去已经选择的rest - arr[index]
            //需要选择的元素个数减一
           int next =  process(arr,index + 1,rest - arr[index],size - 1);
           if (next != -1){
               p2 = arr[index] + next;
           }
        }
        return Math.max(p1,p2);
    }

动态规划.

动态规划就是对数组的改写.递归时有三个变量,因此是个三维数组,
三个步骤,
1.根据base case 初始化dp表
2.递归过程改成从表中拿数据的过程
3.返回递归调用的最初始状态

代码演示

    /**
     * 动态规划
     * @param arr
     * @return
     */
    public static int dp(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        int sum = 0;
        for (int a : arr) {
            sum += a;
        }
        int binarySum = sum / 2;
        int N  = arr.length;
        int M = (N + 1)/2;
        //动态规划表
        int[][][]dp = new int[N + 1][M + 1][binarySum + 1];
        //先把值都初始化为-1
        for (int i = 0; i <= N;i++){
            for (int j = 0;j <= M;j++){
                for (int k = 0;k <= binarySum;k++){
                    dp[i][j][k] = -1;
                }
            }
        }

        //根据base case 初始化已经确定的值
        for (int k = 0;k <= binarySum;k++){
            dp[N][0][k] = 0;
        }
        //递归过程改写
        for (int i = N - 1;i >= 0;i--){
            for (int j = 0;j <= M;j++){
                for (int k = 0 ; k <= binarySum;k++){
                    int p1 = dp[i+1][j][k];
                    int p2 = -1;
                    if (j - 1 >= 0 && arr[i] <= k){
                        //选择时,去下一个位置继续选择index + 1
                        //累加和减去已经选择的rest - arr[index]
                        //需要选择的元素个数减一
                        int next =  dp[i+1][j - 1][k - arr[i]];
                        if (next != -1){
                            p2 = arr[i] + next;
                        }
                    }
                    dp[i][j][k] = Math.max(p1,p2);
                }
            }
        }
        //数组长度不同时的情况讨论
        if (N % 2 == 0){
            return dp[0][N / 2][binarySum];
        }else{
            return Math.max(dp[0][N / 2][binarySum],dp[0][N / 2 + 1][binarySum]);
        }

    }

动态规划专题

将数组分成两个数组,并最小化数组和的差

leetcode337. 打家劫舍 III

整数拆分问题

leetcode213. 打家劫舍 II

leetcode198. 打家劫舍

leetcode174. 地下城游戏

打败怪兽的概率

leetcode688. 骑士在棋盘上的概率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值