将数组分为均匀的两个数组并输出

将一个数组按照总和平均分成均匀的两个数组,并输出.
首先先计算出sum,然后判断sum%2.如果是奇数直接输出-1
应用01背包问题的解决思路.

该问题可以拆分为多个问题来:
1.将一个数组平均拆分为均匀的两个数组
2.将一个数组拆分为两个数组使得两个数组的差值最小

这是问题2:将一个数组拆分为两个数组使得两个数组的差值最小 的完整实现
使用01背包问题可以先求得’‘使得两个数组的差值最小’'的对应子数组的总和是多少

//注意target就是数组总和的一半:sum/2
//dp[i][j]表示从数组的[0,i]下标范围内选取若干个正整数,达到总和为j的最小总和
public static int func(int[] nums,int target){
        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];
                }
            }

        }

        for (int i=0;i<dp.length;i++){
            System.out.println(Arrays.toString(dp[i]));
        }
        System.out.println(dp[nums.length][target]);
        return dp[nums.length][target];
    }

通过这个总和继续使用动态规划01背包 求子数组的元素

//注意target就是数组总和的一半:sum/2
//dp[i][j]表示从数组的[0,i]下标范围内选取若干个正整数(可以是0个)
//是否存在一种选取方案使得被选取的正整数的和等于j
//这里的dp[n][target]就是能不能取到
public static boolean[][] func2(int[]nums,int target){
        int n = nums.length;
        boolean[][] dp = new boolean[n + 1][target + 1];
        // 如果不选取任何正整数,则被选取的正整数等于 0。因此对于所有 0≤i<n,都有 dp[i][0]=true
        for (int i = 0; i <= n; i++) {
            dp[i][0] = true;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= target; j++) {
                if (j - nums[i - 1] < 0) {//说明没办法取到j-nums[i-1]这个数
                    dp[i][j] = dp[i - 1][j];
                } else {//只要前面i-1个数能取到j  或者 之前i-1个数能取到j-nums[i-1]即可
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i - 1]];
                }
            }
        }
        for (int i=0;i<dp.length;i++){
            System.out.println(Arrays.toString(dp[i]));
        }
        return dp;
    }

通过这个动态规划的结果,可以求子数组的元素.

boolean[][] booleans = func2(nums, ans);
        List<Integer> list = new ArrayList<>();
        int target = ans;

        //寻找路径 从原来的boolean中先找target位置是否有满足的条件,有的话加入list,然后更新target
        int n = nums.length;
        while (target>0){
            for (int i=1;i<=n;i++){
                if (booleans[i][target]){
                    list.add(nums[i-1]);
                    //System.out.println(nums[i-1]);
                    target-=nums[i-1];
                    n=i-1;//之后的n只能从前面的糖果袋中取
                    break;
                }
            }
        }

完整的代码如下:

package huatielu;/*
@author Oblak
@date 2022/4/14
@description 可以求得:将一个数组拆分为两个数组使得两个数组的差值最小
求出对应的两个数组
*/

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

public class bag01 {
    public static void main(String[] args) {
        int[] nums = new int[]{1,3,5,7,5,2,5};
        int sum = 0;
        HashMap<Integer,Integer> map= new HashMap<>();
        for (int i:nums){
            sum+=i;
            map.put(i,map.getOrDefault(i,0)+1);
        }
        int ans = func(nums, sum / 2);

        boolean[][] booleans = func2(nums, ans);
        List<Integer> list = new ArrayList<>();
        int target = ans;

        //寻找路径 从原来的boolean中先找target位置是否有满足的条件,有的话加入list,然后更新target
        int n = nums.length;
        while (target>0){
            for (int i=1;i<=n;i++){
                if (booleans[i][target]){
                    list.add(nums[i-1]);
                    //System.out.println(nums[i-1]);
                    target-=nums[i-1];
                    n=i-1;//之后的n只能从前面的糖果袋中取
                    break;
                }
            }
        }

        for (int i=0;i<list.size();i++){
            if (i==0) System.out.print(list.get(0));
            else System.out.print(" "+list.get(i));
            map.put(list.get(i),map.get(list.get(i))-1);
        }
        System.out.println();

        List<Integer> list2 = new ArrayList<>();
        for (int i=0;i<nums.length;i++){
            if (map.get(nums[i])>0){
                list2.add(nums[i]);
                map.put(nums[i],map.get(nums[i])-1);
            }
        }

        for (int i=0;i<list2.size();i++){
            if (i==0) System.out.print(list2.get(0));
            else System.out.print(" "+list2.get(i));
        }

    }

    public static int func(int[] nums,int target){
        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];
                }
            }

        }

        for (int i=0;i<dp.length;i++){
            System.out.println(Arrays.toString(dp[i]));
        }
        System.out.println(dp[nums.length][target]);
        return dp[nums.length][target];

    }

    public static boolean[][] func2(int[]nums,int target){
        int n = nums.length;
        boolean[][] dp = new boolean[n + 1][target + 1];
        // 如果不选取任何正整数,则被选取的正整数等于 0。因此对于所有 0≤i<n,都有 dp[i][0]=true
        for (int i = 0; i <= n; i++) {
            dp[i][0] = true;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= target; j++) {
                if (j - nums[i - 1] < 0) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i - 1]];
                }
            }
        }
        for (int i=0;i<dp.length;i++){
            System.out.println(Arrays.toString(dp[i]));
        }
        return dp;
    }
}

对于问题1:将一个数组平均拆分为均匀的两个数组 其实是一个特例 只需要在刚才的代码中添加一些判断即可.

package huatielu;/*
@author Oblak
@date 2022/4/13
@description 
*/
import org.junit.Test;

import java.util.*;

public class Main3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];

        int sum = 0;
        HashMap<Integer,Integer> map = new HashMap<>();
        for (int i=0;i<n;i++){
            nums[i] = sc.nextInt();
            sum+=nums[i];
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }

        if (sum%2==1){
            System.out.println(-1);
        }else{

            boolean[][] booleans = func2(nums, sum / 2);

            //如果booleans[i][target]都无法满足 则返回-1
            boolean flag = booleans[0][sum/2];
            for (int i=1;i<booleans.length;i++){
                flag = flag||booleans[i][sum/2];
                if (flag) break;
            }

            if (!flag){
                System.out.println(-1);
                return;
            }

            int ans = sum/2;
            List<Integer> list = new ArrayList<>();
            int target = ans;

            //寻找路径 从原来的boolean中先找target位置是否有满足的条件,有的话加入list,然后更新target
            while (target>0){
                for (int i=1;i<=n;i++){
                    if (booleans[i][target]){
                        list.add(nums[i-1]);
                        //System.out.println(nums[i-1]);
                        target-=nums[i-1];
                        n=i-1;//之后的n只能从前面的糖果袋中取
                        break;
                    }
                }
            }

            System.out.println(ans);

            //从大到小排个序
            Collections.sort(list, new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2-o1;
                }
            });

            for (int i=0;i<list.size();i++){
                if (i==0) System.out.print(list.get(0));
                else System.out.print(" "+list.get(i));
                map.put(list.get(i),map.get(list.get(i))-1);
            }
            System.out.println();

            List<Integer> list2 = new ArrayList<>();
            for (int i=0;i<nums.length;i++){
                if (map.get(nums[i])>0){
                    list2.add(nums[i]);
                    map.put(nums[i],map.get(nums[i])-1);
                }
            }

            Collections.sort(list2, new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2-o1;
                }
            });
            for (int i=0;i<list2.size();i++){
                if (i==0) System.out.print(list2.get(0));
                else System.out.print(" "+list2.get(i));
            }
        }

    }

    //这个函数可以得到一个数组平均分成两等分的情况  (01背包问题)
    public static int[][] func(int[] nums,int target){
        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];
                }
            }

        }

        for (int i=0;i<dp.length;i++){
            System.out.println(Arrays.toString(dp[i]));
        }
        System.out.println(dp[nums.length][target]);
        return dp;

    }
    public static boolean[][] func2(int[]nums,int target){
        int n = nums.length;
        boolean[][] dp = new boolean[n + 1][target + 1];
        // 如果不选取任何正整数,则被选取的正整数等于 0。因此对于所有 0≤i<n,都有 dp[i][0]=true
        for (int i = 0; i <= n; i++) {
            dp[i][0] = true;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= target; j++) {
                if (j - nums[i - 1] < 0) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i - 1]];
                }
            }
        }
//        for (int i=0;i<dp.length;i++){
//            System.out.println(Arrays.toString(dp[i]));
//        }
        return dp;
    }



}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值