day50 计算机考研408真题2016年43题(Java)(动图演示)

409 阅读2分钟

题目来源: 计算机考研408真题2016年43题

题目描述:

  • 描述: 43.已知由n(n≥2)个正整数构成的集合A={ak0k<na_k|0≤k<n},将其划分为两个不相交的子集A;和A2,元素个数分别是n1和n2,A1和A2中元素之和分别为S1和S2。设计一个尽可能高效的划分算法,满足|n1-n2|最小且|S1-S2|最大。
    要求:
    1)给出算法的基本设计思想。
    2)根据设计思想,采用C、C++或Java语言描述算法,关键之处给出注释.
    3)说明你所设计算法的时间复杂度和空间复杂度。

思路:基于快速排序思想

  • 因为|n1-n2|≥0,所以想要|n1-n2|最小,只有让A1和A2的元素个数尽可能接近
    • 若A的元素个数n为奇数,那么找到中间数,是该数所在位置的左右两边的元素个数相同
      • 且要将中间数归右边,因为还要保证|S1-S2|最大
    • 若A的元素个数n为偶数,则将其一分为二,使得两部分元素个数相同
  • 使用快速排序的思想,基于枢轴将n个整数划分为两个子集,根据划分后枢轴所处的位置i分别处理:
    • 1.若i=n/2\lfloor n/2 \rfloor,则分组完成,算法结束;
    • 2.若i<n/2\lfloor n/2 \rfloor,则枢轴及之前的所有元素均属于A1,继续对i之后的元素进行划分;
    • 3.若i>n/2\lfloor n/2 \rfloor,则枢轴及之后的所有元素均属于A2,继续对i之前的元素进行划分:
  • 图示:
    • 408_16_43动态演示2.gif
  • 基于快速排序的思想,分为前后两个子集,而不需要对全部元素进行全排序

具体实现:

public class day408_16 {

    static int setPartition(int a[],int n){
        int pivotKey,low=0,low0=0,high=n-1,high0=n-1,flag=1,k=n/2,i;
        int s1=0,s2=0;
        while(flag==1){   //java中的0和1并不代表布尔值中的FALSE与TRUE,与C++不同
            pivotKey = a[low];             //选择枢轴
            while (low<high){              //基于枢轴对数据进行划分
                while(low<high&&a[high]>=pivotKey)
                    --high;
                if(low!=high)
                    a[low]=a[high];
                while(low<high&&a[high]<=pivotKey)
                    ++low;
                if(low!=high)
                    a[high]=a[low];
            }
            a[low]=pivotKey;
            if(low==k-1)            //如果枢轴是第n/2个元素,划分成功
                flag = 0;
            else{
               if(low<k-1){
                   low0 = ++low;
                   high = high0;
               }else{
                   high0 = --high;
                   low = low0;
               }
            }
        }
        for (i = 0; i < k; i++) {
            s1+=a[i];
        }
        for (i = k; i < n; i++) {
            s2+=a[i];
        }
        return s2-s1;

    }

    public static void main(String[] args) {  //测试
        int[] arr = {1,5,9,7,3};    //最终划分为1 3 和 5 9 7 
        int res = setPartition(arr,5);
        System.out.println(res);   //|S1-S2|结果为17
    }
}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 24 天,点击查看活动详情