人力安排(华为od机考题)

一、题目

1.原题

部门在进行需求开发时需要进行人力安排。

当前部门需要完成N个需求,
需求用requirements[]表示,
requirements[i]表示第i个需求的工作量大小,单位:人月
这部分需求需要在M个月内完成开发
进行人力安排后每个月的人力是固定的。

目前要求每个月最多有2个需求开发,
并且每个月需要完成的需求不能超过部门人力。

请帮部门评估在满足需求开发进度的情况下,
每个月需要的最小人力是多少

2.题目理解

已知:

①当前部门要在M个月完成N个需求(requirements);

②每个需求(requirements[i]=x:该需求工作量为x人月,eg:某需求需要3个人做3个月,工作量就是3×3=9人月);

约束:

③每个月最多有两个需求开发(0≤N/M≤2)=>N≤2M;

④未知量:当前部门人力;

⑤每个月需要完成的需求不能超过部门人力:;

(以上两条可以忽略,因为已知条件才与每月所需最小人力相关,④和⑤只是为了补全现实考虑;当然为了更完善也可以设置部门人力,判断是否可以接这个阶段项目。)

求解:

⑥满足需求开发进度的情况下,每个月需要的最小人力。

每个月所需的最小人力是指,为了在给定的月份内完成所有需求,每个月分配的工作量的最小上限。如果每个月的人力小于这个最小值,那么就无法在规定的月份内完成所有需求。

二、思路与代码过程

1.思路

2.代码过程

①main函数

public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入当前项目所需月数:");
        int M = sc.nextInt();
        System.out.println("请输入当前部门在M个月需要完成的需求数:");
        int N = sc.nextInt();
        if (N>2*M){
            System.out.println("当前需求和时间安排不合理");
            return;
        }
        System.out.println("请输入每个需求的工作量大小:");
        int[] requirements = new int[N];
        for (int i = 0; i < N; i++) {
            requirements[i] = sc.nextInt();
        }
        System.out.println(Arrays.toString(requirements));
        int[] sortedRequirements = Arrays.stream(requirements)
                .sorted()
                .toArray();
        System.out.println(Arrays.toString(sortedRequirements));
        //最小值是一个月做一个需求,人数刚好能满足最低需求
        int minRequirement = Arrays.stream(requirements).max().orElseThrow();
        //最大值是一个月做完所有需求,人力需要满足工作量之和
        int maxRequirement = Arrays.stream(requirements).sum();
        System.out.println("下限:"+minRequirement);
        System.out.println("上限:"+maxRequirement);
        //二分法
        int midRequirement = (maxRequirement+minRequirement)/2;
        System.out.println("中间值:"+midRequirement);

        ArrayList<Integer> list = Arrays.stream(sortedRequirements)  // 创建一个 IntStream
                .boxed()                                 // 将 IntStream 转换为 Stream<Integer>
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(list);//

        int lowestHuman = CountHum(M,list,minRequirement,midRequirement,maxRequirement);
        System.out.println("在满足需求开发进度的情况下,每个月需要的最小人力是:"+lowestHuman);
        System.out.println("请输入当前部门人力数量:");
        int P = sc.nextInt();
        if (P>=lowestHuman){
            System.out.println("当前项目部门可以承接。");
        }else {
            System.out.println("当前项目部门不可以承接。");
        }
    }

②全局变量

 static int comWorkmax =Integer.MAX_VALUE;

③CountHum函数

/*
        工作量(人月)=所需人数*所需月数
        总工作量=sum(requirements)=requirements[0]+..+requirements[n-1]
        在组合的工作量小于等于当前mid的值的前提下,对需求进行组合
        组合(最多含有2个需求):re[x]+re[y]
        总工作量=组合1+组合2+..
        将当前需求列表按照小于当前mid的要求组合完之后,输出组合数目,即所需月数month
        若month小于等于需要完成任务的月份,则继续缩小mid
        在mid的前提下继续组合,直到找到最小的
         */
private static int CountHum(int m,ArrayList<Integer> Requirements,int minRequirement,int midRequirement,int maxRequirement) {
       
        int month = Integer.MAX_VALUE;
        ArrayList<Integer> requirements = new ArrayList<>(Requirements);
        //贪心算法:找到组合起来最接近mid的组合
        //eg:mid=29,找两个组合起来的值,或一个比他小而且接近它的排序
        //先比大小mid是否大于最大值,是的话采用两个数的组合,且组合值不大于29
        //若等于最大值,则组合1:最大值,组合二继续寻找
        //因为边界设定不可能小于最大值!!!
        //eg:组合1:9+12=21,移除这两个需求,
        // 找组合2:继续找两个合起来小于29且当前最大的
        //组合3重复以上,直到需求被全部安排好
        //输出此时的组合数量
        //若满足小于等于M,则继续调小
        ArrayList<int[]> combinations= new ArrayList<>();
        ArrayList<int[]> Combination = findCombine(midRequirement,requirements,combinations);
        month = Combination.size();
        comWorkmax = Combination.get(0)[0]+Combination.get(0)[1];
        //组合完成后,看花费月数,若月数满足则继续分割
        if(month<m) {//每个月分太多了,完成太快
            midRequirement = (minRequirement + midRequirement) / 2;
            maxRequirement = (minRequirement + maxRequirement) / 2;
            //min不改变
            CountHum(m,requirements,minRequirement,midRequirement,maxRequirement);

        }
        else if (month>m) {//每个月分太少了,完成太慢
            midRequirement = (midRequirement+ maxRequirement)/2;
            minRequirement = (minRequirement+maxRequirement)/2;
            //max不改变
            CountHum(m,requirements,minRequirement,midRequirement,maxRequirement);

        } else if (month==m) {
            midRequirement = (midRequirement+ minRequirement)/2;
            maxRequirement = (minRequirement + maxRequirement) / 2;
            ArrayList<Integer> requirements1 = new ArrayList<>(Requirements);
            ArrayList<int[]> combinations1= new ArrayList<>();
            ArrayList<int[]> Combination1 = findCombine(midRequirement,requirements1,combinations1);
            int month1 = Combination1.size();
            if(month1==m){
                int comWorkmax1 = Combination1.get(0)[0]+Combination1.get(0)[1];
            if(comWorkmax1<comWorkmax){
                CountHum(m,requirements,minRequirement,midRequirement,maxRequirement);
            }else if(comWorkmax1==comWorkmax){
                 return comWorkmax;
                }
            }else{
                 return comWorkmax;
            }
        }
        
        int minComWorkmax = Integer.MAX_VALUE;
        minComWorkmax = Math.min(minComWorkmax,comWorkmax);
        return minComWorkmax;
    }

④findCombine函数

 //贪心算法:找到组合起来最接近mid的组合
    //eg:mid=29,找两个组合起来的值,或一个比他小而且接近它的排序
    //因为边界设定不可能小于最大值!!!
    //先看是否有两个数组合小于等于mid,是的话采用两个数的组合,并移除
    //若当前列表,没有两个数组合小于等于mid,就按差值最小依次加入
    //eg:组合1:9+12=21,移除这两个需求,
    // 找组合2:继续找两个合起来小于29且当前最大的
    //组合3重复以上,直到需求被全部安排好
    //输出此时的组合数量
    //若满足小于等于M,则继续调小

    private static ArrayList<int[]> findCombine(int midRequirement, ArrayList<Integer> List,ArrayList<int[]> combination) {
        ArrayList<Integer> list = new ArrayList<>(List);

        int smallestDifference = Integer.MAX_VALUE;
        //空了就返回
        if (!list.isEmpty()){
            //若仍有元素
            if (list.size() >= 1) {
                //剩下一个直接加入
                if (list.size() == 1) {
                    combination.add(new int[]{0,list.get(0)});
                    list.remove(0);
                    findCombine(midRequirement, list,combination);
                } else if(list.size() >= 2) {
                    //list有起码两个元素,寻找组合可能
                    int[] combine1 = new int[2];

                    int currentListSize = list.size();
                    boolean noCombination = true;
                    ArrayList<Integer> NoCombination = new ArrayList<>();

                    for (int i = 0; i < list.size(); i++) {
                        for (int j = i + 1; j < list.size(); j++) {//避免自身重复
                            int combines = list.get(i) + list.get(j);
                            int difference = midRequirement - combines;
                            if (difference >= 0) {
                                noCombination = false;
                                int currentDifference = smallestDifference;
                                smallestDifference = Math.min(smallestDifference, difference);
                                if (smallestDifference!=currentDifference) {
                                    combine1[0] = list.get(i);
                                    combine1[1] = list.get(j);
                                }
                            }
                        }
                        //一轮组合后没有组合成功,说明当前数和任意一个数都无法组合,只能solo,加入不能组合表
                        if (noCombination){
                            NoCombination.add(list.get(i));
                            combination.add(new int[]{list.get(i),0});
                           
                            }
                        }
                    }
                    if (combine1[1]!=0){//处理有组合的
                        combination.add(combine1);
                        list.remove(Integer.valueOf(combine1[0]));
                        list.remove(Integer.valueOf(combine1[1]));
                        findCombine(midRequirement, list,combination);
                    }
                }
            }
        }
        return combination;
    }

三、运行结果

1.运行截图

2.带数据分析运行结果

请输入当前项目所需月数:
3
请输入当前部门在M个月需要完成的需求数:
4
请输入每个需求的工作量大小:
3 5 3 4
[3, 5, 3, 4]
[3, 3, 4, 5]
下限:5
上限:15
中间值:10
[3, 3, 4, 5]
**********************************CountNum start************************************************
countnum requirment:[3, 3, 4, 5]
--------------findCombine---------------------
当前mid为:10
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:3
当前组合值为:6,当前差值为:4
最小差值:4
循环内 当前最小差值组合:3,3
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:3
最小差值:3
循环内 当前最小差值组合:3,4
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:2
最小差值:2
循环内 当前最小差值组合:3,5
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:3
最小差值:2
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:2
最小差值:2
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:1
最小差值:1
循环内 当前最小差值组合:4,5
--------j循环结束·--------
--------------------i循环------------------------
--------j循环结束·--------
---------------i循环结束-------------------
[]

-------------循环外--------- 
当前最小差值组合:4,5
当前组合1:[4, 5]
当前list:[3, 3]
--------------findCombine---------------------
当前mid为:10
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:3
当前组合值为:6,当前差值为:4
最小差值:4
循环内 当前最小差值组合:3,3
--------j循环结束·--------
--------------------i循环------------------------
--------j循环结束·--------
---------------i循环结束-------------------
[]

-------------循环外--------- 
当前最小差值组合:3,3
当前组合1:[3, 3]
当前list:[]
--------------findCombine---------------------
当前mid为:10
*****************CountNum中的findcombine之后**********************

CountNum中的组合:
[4, 5]
[3, 3]
组合中一个月最大工作量为comWorkmax:9
2
===============[3, 3, 4, 5]
当前mid为:10,只需要2个月,小于3个月,每个月分太多了,完成太快。
要求月数:3,下限:5,中间值:7,上限:10
**********************************CountNum start************************************************
countnum requirment:[3, 3, 4, 5]
--------------findCombine---------------------
当前mid为:7
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:3
当前组合值为:6,当前差值为:1
最小差值:1
循环内 当前最小差值组合:3,3
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:0
最小差值:0
循环内 当前最小差值组合:3,4
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-1
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:0
最小差值:0
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-1
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:-2
--------j循环结束·--------
--------------------i循环------------------------
--------j循环结束·--------
---------------i循环结束-------------------
[]

-------------循环外--------- 
当前最小差值组合:3,4
当前组合1:[3, 4]
当前list:[3, 5]
--------------findCombine---------------------
当前mid为:7
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-1
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:3,需要单走。

组合不成功后的com[3, 4]
组合不成功后的com[3, 0]
[3]
--------------------i循环------------------------
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:5,需要单走。

组合不成功后的com[3, 4]
组合不成功后的com[3, 0]
组合不成功后的com[5, 0]
[3, 5]
---------------i循环结束-------------------
[3, 5]
*****************CountNum中的findcombine之后**********************

CountNum中的组合:
[3, 4]
[3, 0]
[5, 0]
组合中一个月最大工作量为comWorkmax:7
3
===============[3, 3, 4, 5]
当前mid为:7需要3个月,等于3个月,进度刚刚好,可以继续缩小寻找
要求月数:3,下限:5,中间值:6,上限:7
--------------findCombine---------------------
当前mid为:6
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:3
当前组合值为:6,当前差值为:0
最小差值:0
循环内 当前最小差值组合:3,3
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:-1
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-2
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:-1
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-2
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:-3
--------j循环结束·--------
--------------------i循环------------------------
--------j循环结束·--------
---------------i循环结束-------------------
[]

-------------循环外--------- 
当前最小差值组合:3,3
当前组合1:[3, 3]
当前list:[4, 5]
--------------findCombine---------------------
当前mid为:6
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:-3
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:4,需要单走。

组合不成功后的com[3, 3]
组合不成功后的com[4, 0]
[4]
--------------------i循环------------------------
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:5,需要单走。

组合不成功后的com[3, 3]
组合不成功后的com[4, 0]
组合不成功后的com[5, 0]
[4, 5]
---------------i循环结束-------------------
[4, 5]
*****************month1==m且comwork1<comwork中的findcombine之后**********************

CountNum中的组合:
[3, 3]
[4, 0]
[5, 0]
month1:3,当前m为:3
当前comWorkmax1为:6,当前comWorkmax为;7
comWorkmax1<comWorkmax
**********************************CountNum start************************************************
countnum requirment:[3, 3, 4, 5]
--------------findCombine---------------------
当前mid为:6
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:3
当前组合值为:6,当前差值为:0
最小差值:0
循环内 当前最小差值组合:3,3
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:-1
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-2
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:-1
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-2
--------j循环结束·--------
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:-3
--------j循环结束·--------
--------------------i循环------------------------
--------j循环结束·--------
---------------i循环结束-------------------
[]

-------------循环外--------- 
当前最小差值组合:3,3
当前组合1:[3, 3]
当前list:[4, 5]
--------------findCombine---------------------
当前mid为:6
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:-3
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:4,需要单走。

组合不成功后的com[3, 3]
组合不成功后的com[4, 0]
[4]
--------------------i循环------------------------
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:5,需要单走。

组合不成功后的com[3, 3]
组合不成功后的com[4, 0]
组合不成功后的com[5, 0]
[4, 5]
---------------i循环结束-------------------
[4, 5]
*****************CountNum中的findcombine之后**********************

CountNum中的组合:
[3, 3]
[4, 0]
[5, 0]
组合中一个月最大工作量为comWorkmax:6
3
===============[3, 3, 4, 5]
当前mid为:6需要3个月,等于3个月,进度刚刚好,可以继续缩小寻找
要求月数:3,下限:5,中间值:5,上限:6
--------------findCombine---------------------
当前mid为:5
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:3
当前组合值为:6,当前差值为:-1
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:-2
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-3
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:3,需要单走。

组合不成功后的com[3, 0]
[3]
--------------------i循环------------------------
--------j循环--------
当前i为:3,当前j为:4
当前组合值为:7,当前差值为:-2
--------j循环--------
当前i为:3,当前j为:5
当前组合值为:8,当前差值为:-3
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:3,需要单走。

组合不成功后的com[3, 0]
组合不成功后的com[3, 0]
[3, 3]
--------------------i循环------------------------
--------j循环--------
当前i为:4,当前j为:5
当前组合值为:9,当前差值为:-4
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:4,需要单走。

组合不成功后的com[3, 0]
组合不成功后的com[3, 0]
组合不成功后的com[4, 0]
[3, 3, 4]
--------------------i循环------------------------
--------j循环结束·--------
-------------------没组合判断------------------------

组合不成功,当前值:5,需要单走。

组合不成功后的com[3, 0]
组合不成功后的com[3, 0]
组合不成功后的com[4, 0]
组合不成功后的com[5, 0]
[3, 3, 4, 5]
---------------i循环结束-------------------
[3, 3, 4, 5]
*****************month1==m且comwork1<comwork中的findcombine之后**********************

CountNum中的组合:
[3, 0]
[3, 0]
[4, 0]
[5, 0]
month1:4,当前m为:3
当前值不可以再缩小了!
最低每月人力需求为:comWorkmax:6,我返回啦
************************************end**********************************************
6
************************************end**********************************************
6
在满足需求开发进度的情况下,每个月需要的最小人力是:6
请输入当前部门人力数量:
8
当前项目部门可以承接。
 

3.带数据分析完整代码

import java.util.*;
import java.util.stream.Collectors;

public class test32 {
    public static void main(String[] args) {

        ///*
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入当前项目所需月数:");
        int M = sc.nextInt();
        System.out.println("请输入当前部门在M个月需要完成的需求数:");
        int N = sc.nextInt();
        if (N>2*M){
            System.out.println("当前需求和时间安排不合理");
            return;
        }
        System.out.println("请输入每个需求的工作量大小:");
        int[] requirements = new int[N];
        for (int i = 0; i < N; i++) {
            requirements[i] = sc.nextInt();
        }
        System.out.println(Arrays.toString(requirements));
        int[] sortedRequirements = Arrays.stream(requirements)
                .sorted()
                .toArray();
        System.out.println(Arrays.toString(sortedRequirements));
        //最小值是一个月做一个需求,人数刚好能满足最低需求
        int minRequirement = Arrays.stream(requirements).max().orElseThrow();
        //最大值是一个月做完所有需求,人力需要满足工作量之和
        int maxRequirement = Arrays.stream(requirements).sum();
        System.out.println("下限:"+minRequirement);
        System.out.println("上限:"+maxRequirement);
        //二分法
        int midRequirement = (maxRequirement+minRequirement)/2;
        System.out.println("中间值:"+midRequirement);

        ArrayList<Integer> list = Arrays.stream(sortedRequirements)  // 创建一个 IntStream
                .boxed()                                 // 将 IntStream 转换为 Stream<Integer>
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(list);//

        int lowestHuman = CountHum(M,list,minRequirement,midRequirement,maxRequirement);
        System.out.println("在满足需求开发进度的情况下,每个月需要的最小人力是:"+lowestHuman);
        System.out.println("请输入当前部门人力数量:");
        int P = sc.nextInt();
        if (P>=lowestHuman){
            System.out.println("当前项目部门可以承接。");
        }else {
            System.out.println("当前项目部门不可以承接。");
        }
        //*/
        /*复杂测试:
        int M = 5;
        int N = 7;
        int[] requirements = {1 , 9 , 9 , 7 , 3 , 6 , 12};
        //int[] requirements = {5, 2 , 3 ,4 , 5};
        int[] sortedRequirements = Arrays.stream(requirements)
                .sorted()
                .toArray();

       // */

        /*简单测试:
        int M = 3;
        int N = 4;
        int[] requirements = {3,5,3,4};
        //int[] requirements = {5, 2 , 3 ,4 , 5};
        int[] sortedRequirements = Arrays.stream(requirements)
                .sorted()
                .toArray();

        */
/*
        System.out.println(Arrays.toString(requirements));
        //最小值是一个月做一个需求,人数刚好能满足最低需求
        int minRequirement = Arrays.stream(requirements).max().orElseThrow();
        //最大值是一个月做完所有需求,人力需要满足工作量之和
        int maxRequirement = Arrays.stream(requirements).sum();
        System.out.println("下限:"+minRequirement);
        System.out.println("上限:"+maxRequirement);
        //二分法
        int midRequirement = (maxRequirement+minRequirement)/2;
        System.out.println("中间值:"+midRequirement);

        ArrayList<Integer> list = Arrays.stream(sortedRequirements)  // 创建一个 IntStream
                .boxed()                                 // 将 IntStream 转换为 Stream<Integer>
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(list);//

        int lowestHuman = CountHum(M,list,minRequirement,midRequirement,maxRequirement);
        System.out.println("在满足需求开发进度的情况下,每个月需要的最小人力是:"+lowestHuman);
        */
    }
    /*
         //工作量(人月)=所需人数*所需月数
         //总工作量=sum(requirements)=requirements[0]+..+requirements[n-1]
         //在组合的工作量小于等于当前mid的值的前提下,对需求进行组合
         //组合(最多含有2个需求):re[x]+re[y]
         // 总工作量=组合1+组合2+..
         //将当前需求列表按照小于当前mid的要求组合完之后,输出组合数目,即所需月数month
         //若month小于等于需要完成任务的月份,则继续缩小mid
         //在mid的前提下继续组合,直到找到最小的
         */
    //static int lowestHU = Integer.MAX_VALUE;
    static int comWorkmax =Integer.MAX_VALUE;
    private static int CountHum(int m,ArrayList<Integer> Requirements,int minRequirement,int midRequirement,int maxRequirement) {
        System.out.println("**********************************CountNum start************************************************");
        int month = Integer.MAX_VALUE;
        ArrayList<Integer> requirements = new ArrayList<>(Requirements);
        System.out.println("countnum requirment:"+requirements);//

        //贪心算法:找到组合起来最接近mid的组合
        //eg:mid=29,找两个组合起来的值,或一个比他小而且接近它的排序
        //先比大小mid是否大于最大值,是的话采用两个数的组合,且组合值不大于29
        //若等于最大值,则组合1:最大值,组合二继续寻找
        //因为边界设定不可能小于最大值!!!
        //eg:组合1:9+12=21,移除这两个需求,
        // 找组合2:继续找两个合起来小于29且当前最大的
        //组合3重复以上,直到需求被全部安排好
        //输出此时的组合数量
        //若满足小于等于M,则继续调小
        ArrayList<int[]> combinations= new ArrayList<>();
        ArrayList<int[]> Combination = findCombine(midRequirement,requirements,combinations);
        System.out.println("*****************CountNum中的findcombine之后**********************");
        System.out.println("\nCountNum中的组合:");
        for (int[] combination : Combination) {
            System.out.println(Arrays.toString(combination));
        }
        month = Combination.size();
        comWorkmax = Combination.get(0)[0]+Combination.get(0)[1];
        System.out.println("组合中一个月最大工作量为comWorkmax:"+comWorkmax);
        System.out.println(month);

        System.out.println("==============="+requirements);

        //组合完成后,看花费月数,若月数满足则继续分割
        if(month<m) {//每个月分太多了,完成太快
            System.out.println("当前mid为:"+midRequirement+",只需要" + month + "个月,小于" + m + "个月,每个月分太多了,完成太快。");
            midRequirement = (minRequirement + midRequirement) / 2;
            maxRequirement = (minRequirement + maxRequirement) / 2;
            //min不改变
            System.out.println("要求月数:"+m+",下限:"+minRequirement+",中间值:"+midRequirement+",上限:"+maxRequirement);
            CountHum(m,requirements,minRequirement,midRequirement,maxRequirement);

        }
        else if (month>m) {//每个月分太少了,完成太慢
            System.out.println("当前mid为:"+midRequirement+"需要" + month + "个月,大于" + m + "个月,每个月分太少了,完成太慢。");
            midRequirement = (midRequirement+ maxRequirement)/2;
            minRequirement = (minRequirement+maxRequirement)/2;
            //max不改变
            System.out.println("要求月数:"+m+",下限:"+minRequirement+",中间值:"+midRequirement+",上限:"+maxRequirement);

            CountHum(m,requirements,minRequirement,midRequirement,maxRequirement);

        } else if (month==m) {
            System.out.println("当前mid为:"+midRequirement+"需要" + month + "个月,等于" + m + "个月,进度刚刚好,可以继续缩小寻找");//
            midRequirement = (midRequirement+ minRequirement)/2;
            maxRequirement = (minRequirement + maxRequirement) / 2;
            System.out.println("要求月数:"+m+",下限:"+minRequirement+",中间值:"+midRequirement+",上限:"+maxRequirement);
            ///*
            ArrayList<Integer> requirements1 = new ArrayList<>(Requirements);
            ArrayList<int[]> combinations1= new ArrayList<>();
            ArrayList<int[]> Combination1 = findCombine(midRequirement,requirements1,combinations1);
            System.out.println("*****************month1==m且comwork1<comwork中的findcombine之后**********************");
            System.out.println("\nCountNum中的组合:");
            for (int[] combination : Combination1) {
                System.out.println(Arrays.toString(combination));
            }
            int month1 = Combination1.size();
            System.out.println("month1:"+month1+",当前m为:"+m);
            if(month1==m){
                int comWorkmax1 = Combination1.get(0)[0]+Combination1.get(0)[1];
                System.out.println("当前comWorkmax1为:"+comWorkmax1+",当前comWorkmax为;"+comWorkmax);
            if(comWorkmax1<comWorkmax){
                System.out.println("comWorkmax1<comWorkmax");
                CountHum(m,requirements,minRequirement,midRequirement,maxRequirement);
            }else if(comWorkmax1==comWorkmax){
                System.out.println("当前值不可以再缩小了!");
                System.out.println("最低每月人力需求为:comWorkmax:"+comWorkmax+",我返回啦");
                return comWorkmax;
                }
            }else{
                System.out.println("当前值不可以再缩小了!");
                System.out.println("最低每月人力需求为:comWorkmax:"+comWorkmax+",我返回啦");
                return comWorkmax;
            }
            //*/
        }
        System.out.println("************************************end**********************************************");
        int minComWorkmax = Integer.MAX_VALUE;
        minComWorkmax = Math.min(minComWorkmax,comWorkmax);
        System.out.println(minComWorkmax);
        return minComWorkmax;
    }


    //贪心算法:找到组合起来最接近mid的组合
    //eg:mid=29,找两个组合起来的值,或一个比他小而且接近它的排序
    //因为边界设定不可能小于最大值!!!
    //先看是否有两个数组合小于等于mid,是的话采用两个数的组合,并移除
    //若当前列表,没有两个数组合小于等于mid,就按差值最小依次加入
    //eg:组合1:9+12=21,移除这两个需求,
    // 找组合2:继续找两个合起来小于29且当前最大的
    //组合3重复以上,直到需求被全部安排好
    //输出此时的组合数量
    //若满足小于等于M,则继续调小

    private static ArrayList<int[]> findCombine(int midRequirement, ArrayList<Integer> List,ArrayList<int[]> combination) {
        System.out.println("--------------findCombine---------------------");
        System.out.println("当前mid为:"+midRequirement);
        ArrayList<Integer> list = new ArrayList<>(List);
        //System.out.println("findCom========list:"+list);

        int smallestDifference = Integer.MAX_VALUE;
        //空了就返回
        if (!list.isEmpty()){
            //若仍有元素
            if (list.size() >= 1) {
                //剩下一个直接加入
                if (list.size() == 1) {
                    combination.add(new int[]{0,list.get(0)});
                    list.remove(0);
                    findCombine(midRequirement, list,combination);
                } else if(list.size() >= 2) {
                    //list有起码两个元素,寻找组合可能
                    //System.out.println("findCom==222222======list:"+list);
                    int[] combine1 = new int[2];

                    int currentListSize = list.size();
                    boolean noCombination = true;
                    ArrayList<Integer> NoCombination = new ArrayList<>();

                    for (int i = 0; i < list.size(); i++) {
                        System.out.println("--------------------i循环------------------------");
                        for (int j = i + 1; j < list.size(); j++) {//避免自身重复
                            System.out.println("--------j循环--------");
                            System.out.println("当前i为:" + list.get(i) + ",当前j为:" + list.get(j));
                            int combines = list.get(i) + list.get(j);
                            int difference = midRequirement - combines;
                            System.out.println("当前组合值为:" + combines + ",当前差值为:" + difference);
                            if (difference >= 0) {
                                noCombination = false;
                                int currentDifference = smallestDifference;
                                smallestDifference = Math.min(smallestDifference, difference);
                                System.out.print("最小差值:" + smallestDifference);
                                System.out.println();
                                if (smallestDifference!=currentDifference) {
                                    combine1[0] = list.get(i);
                                    combine1[1] = list.get(j);
                                    System.out.println("循环内 当前最小差值组合:"+combine1[0]+","+combine1[1]);
                                }
                            }
                        }
                        System.out.println("--------j循环结束·--------");
                        //System.out.println("Nom==?????????======Nom:"+NoCombination);
                        //一轮组合后没有组合成功,说明当前数和任意一个数都无法组合,只能solo,加入不能组合表
                        if (noCombination){
                            System.out.println("-------------------没组合判断------------------------");
                           // System.out.println("findCom==33333======list:"+list);
                            NoCombination.add(list.get(i));
                            System.out.println("\n组合不成功,当前值:"+list.get(i)+",需要单走。\n");
                            combination.add(new int[]{list.get(i),0});
                            for (int[] combination1 : combination) {
                                System.out.println("组合不成功后的com"+Arrays.toString(combination1));
                            }
                            System.out.println(NoCombination);
                            //只剩一个时,也要加入!!!
                            /*
                            if (list.size() == 1) {
                                NoCombination.add(list.get(0));
                            }
                             */
                            //System.out.println("findCom==4444444======list:"+list);
                           //System.out.println("NoCom-----------------"+NoCombination);
                        }
                    }
                    System.out.println("---------------i循环结束-------------------");
                    System.out.println(NoCombination);
                    /*
                    if (!NoCombination.isEmpty()){
                        System.out.println("当前失败组合:"+NoCombination);
                        for (int i = 0; i <  NoCombination.size(); i++){
                            System.out.println("\n组合不成功,当前值:"+NoCombination.get(i)+",需要单走。\n");
                            combination.add(new int[]{NoCombination.get(i), 0});

                            for (int[] combination1 : combination) {
                                System.out.println(Arrays.toString(combination1));
                            }
                            NoCombination.remove(NoCombination.get(i));
                            i--;
                            System.out.println("移除后的失败组合"+NoCombination);
                        }

                        for (int[] combination1 : combination) {
                            System.out.println("组合不成功后的com"+Arrays.toString(combination1));
                        }
                        System.out.println("组合不成功后的list"+list);

                        findCombine(midRequirement, list,combination);
                    }
                    */
                    if (combine1[1]!=0){//处理有组合的
                        System.out.println("\n-------------循环外--------- \n" +
                                "当前最小差值组合:"+combine1[0]+","+combine1[1]);
                        System.out.println("当前组合1:"+Arrays.toString(combine1));
                        combination.add(combine1);
                        list.remove(Integer.valueOf(combine1[0]));
                        list.remove(Integer.valueOf(combine1[1]));
                        System.out.println("当前list:"+list);
                        findCombine(midRequirement, list,combination);
                    }

                }
            }
        }
        return combination;
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值