• 阿里在线编程,去除三个元素,四等分数组问题!


    
    
    
    
    
    
    /**
     * Created by lw_co on 2017/3/3.
     */
    import java.io.*;
    import java.util.*;
    import java.text.*;
    import java.math.*;
    import java.util.regex.*;
    
    public class Solution {
    
    /** 请完成下面这个函数,实现题目要求的功能 **/
        /** 当然,你也可以不按照这个模板来作答,完全按照自己的想法来 ^-^  **/
        /**
         *
         * 对于一个长度为N的整型数组A, 数组里所有的数都是正整数,对于两个满足0<=X <= Y <N的整数,A[X], A[X+1] … A[Y]构成A的一个切片,记作(X, Y)。
         用三个下标 m1, m2, m3下标满足条件 0 < m1, m1 + 1 < m2, m2 +1 < m3 < N – 1。
         可以把这个整型数组分成(0, m1-1), (m1+1, m2-1), (m2+1, m3-1), (m3+1, N-1) 四个切片。如果这四个切片中的整数求和相等,称作“四等分”。
         编写一个函数,求一个给定的整型数组是否可以四等分,如果可以,返回一个布尔类型的true,如果不可以返回一个布尔类型的false。
         限制条件: 数组A最多有1,000,000项,数组中的整数取值范围介于-1,000,000到1,000,000之间。
         要求: 函数的计算复杂度为O(N),使用的额外存储空间(除了输入的数组之外)最多为O(N)。
         例子:
         对于数组A=[2, 5, 1, 1, 1, 1, 4, 1, 7, 3, 7] 存在下标 2, 7, 9使得数组分成四个分片[2, 5], [1, 1, 1, 4], [7], [7],这三个分片内整数之和相等,所以对于这个数组,函数应该返回true。
         对于数组 A=[10, 2, 11, 13, 1, 1, 1, 1, 1], 找不到能把数组四等分的下标,所以函数应该返回false。
         */
        /****************************************************/
        /**注意:
         * 1、只删除3个元素,等分为四份。
         * 2、数组元素为正整数。
         * 3、疑问:后面又说整数取值范围介于-1,000,000到1,000,000之间?明显混淆视听,看来阿里的题考查阅读与观察啊!
         *
         * */
        /**方法一:
         * 时间复杂度O(n)
         * 思路:
         * 1、先二等分,去除中间那个元素。至少中间左边还是右边有待考证,但可以肯定超过平均数的第三个数
         *
         * 符号:
         * indexBegin开始索引,indexEnd结束索引。
         * 在sumArr,与chooseRemove中均计算的是indexBegin<=i<indexEnd的元素。
         * 元素分布:0至v1-1,v1+1到v2-1,v2+1到N-1
         *
         * */
    
        /**测试用例:
         * {1,1,1,1,7,1,3,4,1,2,1,5,2,2} true;把7换成10,把4换成1,原式也可等分,但却如下
         * {1,1,1,1,10,1,3,1,1,2,1,5,2,2} false;
         * {2,2,5,1,2,1,1,3,1,10,1,1,1,1} false;上面的倒序。看来与顺序无关,算法还是存在问题,此种解法存在问题。
         *
         * */
    
        /**方法二
         * 时间复杂度O(n)
         * 从两边开始找,找到之后再找中间
         * 技巧,注意到只删除3个元素,又因为要第一分组与第四分组相等.
         * 设等分值为v,第一分组n1与第二分组元素n4个数共为n,则2<=N-3-n<=2v;
         * 2<=N-3-n<=2v,解释:最小还剩下两个位置各为一部分,最大即剩下的就是其它两部分均为1组成,所以最多有2v个1。
         * 对于情况{1,5,3,1,9,1,9,1,3,2,4},该方法不能正确等分,因为还缺少条件。
         *
         * */
        /**方法二纠正与改进
         * 关键找到v的下限。
         * 注意条件,只去除3个元素,也就是说我们可以记下最大的三个元素,或者max元素。
         * 当我们找到第一部分和第四部分相等时(记等分值为 v),相应的分割元素也就知道了,记为d1与d3,而只有中间那个分割元d2还未知。
         * 对于d2有:min<=d2<=max, min与max为数组中的最大与最小值
         * 若能四等分,则有  (sum-2*v-max-d1-d3)/2 <= v=(sum-2*v-d2-d1-d3)/2 <= (sum-2*v-min-d1-d3)/2,即 sum-max <= 4v+d1+d3 <= sum-min
         *
         * 该方法忽略一种情况,即若等号成立满足,max或者min就是中间的d2否则不成立,即在边界条件成立时我们需要单独检测d2是否为min或者max,才能说明能否分割,下面例子说明该缺点。
         *
         * 总结:以前的条件为,2<=N-3-n<=2v 现改变为,sum-max <= 4v+d1+d3 <= sum-min
         *
         * 其它条件:
         * s(中)-max<=s(左)+s(右)<s(中),即 v >=(s(中)-max)/2;
         * (sum-top3)/4<=v
         *
         * 例子缺点:
         *    int[] A={1,5,3,1,9,1,9,1,3,2,4};//false;
         *    int[] A={1,5,3,1,5,9,5,1,3,2,4};//true;
         * 以上两组都可等分。所以该方法还是存在问题。
         * */
        /**方法三
         * 由于方法二还是不能完美解决
         * 但是事先不知道中间那个分割值,只有去验证。验证正确则为true,否则重新找。
         * 在findValLocate中嵌套checkingFind。
         * 只加这么两行,循环体中,在判断sum-max <= 4v+d1+d3 <= sum-min下面
           re[2]=checkingFind(A,re[0],re[1]+1,re[3]-1);
           if(re[2]>0 && (smm[0]-(A[re[1]]+A[re[2]]+A[re[3]]) )/4==v1 ){return re;}
         * 算法复杂度不好分析,最好O(n),最坏(n^2)
         *
         *
         * */
        //方法三开始**********************************************
        static boolean resolve3(int[] A) {
            int[] re=findValLocate3(A);
            System.out.println("寻找完毕,均分值,d1,d2,d3分别为: "+Arrays.toString(re));
            if(re==null){
                return false;
            }
            return true;
        }
        static int[] findValLocate3(int[] A){
            int v1=0,v4=0;
            int[] smm=sumArrMaxMin(A);
            for(int i=0,j=A.length-1;i<j;){
    
                if(v1<v4){
                    v1=v1+A[i];
                    ++i;
                }else if(v1>v4){
                    v4=v4+A[j];
                    --j;
                }else{
                    /*验证:sum-max <= 4v+d1+d3 <= sum-min*/
                    int m=4*v1+A[i]+A[j];
                    if((smm[0]-smm[2]) <= m && m<= (smm[0]-smm[1]) ){
    
                        int re[]={v1,i,0,j};
                        /*区别于方法二的地方*/
                        re[2]=checkingFind(A,re[0],re[1]+1,re[3]-1);
                        //System.out.println("re值="+Arrays.toString(re));
                        if(re[2]>0 && (smm[0]-(A[re[1]]+A[re[2]]+A[re[3]]) )/4==v1 ){
                            return re;
                            /*if((smm[0]-smm[2]) == m){
                                if( A[re[2]] ==smm[2]){
                                    return re;
                                }
                            }else if(m== (smm[0]-smm[1]) ){
                                if( A[re[2]] ==smm[1]){
                                    return re;
                                }
                            }*/
                        }
    
                    }
                     v1=v1+A[i];
                     ++i;
                    //System.out.println(i);
    
                    /*改进前:
                    //验证:2<=N-3-n<=2v
                    int m=A.length-3-(i+1+A.length-j);
                    if(m>=2 && m<=2*v1 ){
                        //这里返回的是去除点的位置,i,j没有加减,
                        //是因为以前的操作都让它向后移了一位了,
                        //现在指的就是要去除的点
                        int re[]={v1,i,0,j};
                        return re;
                    }else{
                        v1=v1+A[i];
                        ++i;
                    }*/
    
                }
            }
            return null;
        }
    
        //方法三结束**********************************************
        //方法二开始**********************************************
        static boolean resolve2(int[] A) {
            int[] re=findValLocate(A);
    
            System.out.println("寻找完毕,开始检查: "+Arrays.toString(re));
            re[2]=checkingFind(A,re[0],re[1]+1,re[3]-1); //减1是由于有4部分,最后一部分至少占用1个位置。
            System.out.println("检查: "+Arrays.toString(re));
            int v3=checkingFind(A,re[0],re[2]+1,re[3]);//检查第四部分,的分割点是否为re[3]
            if(v3==re[3]){
                return true;
            }
            return false;
        }
        static int checkingFind(int[] A ,int val,int begin,int end){
            int s=0;
            for(int i=begin;i<end;++i){
                s=s+A[i];
                if(s==val){
                    //返回要去除那个点。
                    return i+1;
                }
            }
            return -1;
        }
        /*返回均分值,与要去除的第一个和第三个位置*/
        static int[] findValLocate(int[] A){
            int v1=0,v4=0;
            int[] smm=sumArrMaxMin(A);
            for(int i=0,j=A.length-1;i<j;){
    
                if(v1<v4){
                    v1=v1+A[i];
                    ++i;
                }else if(v1>v4){
                    v4=v4+A[j];
                    --j;
                }else{
                    /*验证:sum-max <= 4v+d1+d3 <= sum-min*/
                    int m=4*v1+A[i]+A[j];
                    if((smm[0]-smm[2]) <= m && m<= (smm[0]-smm[1]) ){
                        int re[]={v1,i,0,j};
                        return re;
                    }else{
                        v1=v1+A[i];
                        ++i;
                    }
                    /*改进前:
                    //验证:2<=N-3-n<=2v
                    int m=A.length-3-(i+1+A.length-j);
                    if(m>=2 && m<=2*v1 ){
                        //这里返回的是去除点的位置,i,j没有加减,
                        //是因为以前的操作都让它向后移了一位了,
                        //现在指的就是要去除的点
                        int re[]={v1,i,0,j};
                        return re;
                    }else{
                        v1=v1+A[i];
                        ++i;
                    }*/
    
                }
            }
            return null;
        }
        static int[] sumArrMaxMin(int[] A){
            int sum=0,max=0,min=Float.MAX_EXPONENT;
            for(int i=0; i<A.length;++i){
                sum =sum +A[i];
                if(max<A[i]){
                    max=A[i];
                }
                if(min>A[i]){
                    min=A[i];
                }
            }
            int[] r={sum,min,max};
            return r;
        }
        //方法二结束**********************************************
        //方法一开始**********************************************
        static boolean resolve(int[] A) {
            int v2=chooseRemove(A,0,A.length-1);
            int v1=chooseRemove(A,0,v2-1);
            int v3=chooseRemove(A,v2+1,A.length-1);
            int s1=sumArr(A,0,v1-1);
            int s2=sumArr(A,v1+1,v2-1);
            int s3=sumArr(A,v2+1,v3-1);
            int s4=sumArr(A,v3+1,A.length-1);
    
            System.out.println("去除的元素依次是:A["+v1+"]="+A[v1]+" ; "
                                                +"A["+v2+"]="+A[v2]+" ; "
                                                +"A["+v3+"]="+A[v3]+" ; ");
            System.out.println("四个部分和是:"+"s1="+s1+" ; "
                                                +"s2="+s2+" ; "
                                                +"s3="+s3+" ; "
                                                +"s4="+s4+" ; ");
            if(s1==s2&&s3==s4&&s2==s3){
                return true;
            }
    
        return false;
    
        }
        static int sumArr(int[] A, int indexBegin, int indexEnd){
            int sum=0;
            for(int i=indexBegin; i<=indexEnd;++i){
                sum =sum +A[i];
            }
            return sum;
        }
        static int chooseRemove(int[] A, int indexBegin, int indexEnd){
            int ave=sumArr(A,indexBegin,indexEnd)/2;
            int val=0;
            for(int i=indexBegin;i<=indexEnd;++i){
                val=val+A[i];
                if(val>ave){
                    return i;
                }
            }
            return -1;
        }
        //方法一结束**********************************************
    
        public static void main(String[] args){
            /*ArrayList<Integer> inputs = new ArrayList<Integer>();
            Scanner in = new Scanner(System.in);
            String line = in.nextLine();
            while(line != null && !line.isEmpty()) {
                int value = Integer.parseInt(line.trim());
                if(value == 0) break;
                inputs.add(value);
                line = in.nextLine();
            }
            int[] A = new int[inputs.size()];
            for(int i=0; i<inputs.size(); i++) {
                A[i] = inputs.get(i).intValue();
            }*/
            //int[] A={1,1,1,1,7,1,3,4,1,2,1,5,2,2};
            //int[] A={1,1,1,1,10,1,3,1,1,2,1,5,2,2};
            //int[] A={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
            //int[] A={1,2,3,3,2,8,1,4,9,5,3,2,4};
            //int[] A={1,5,3,2,4,1,1,5,3,2,4};
              int[] A={1,5,3,1,9,1,9,1,3,2,4};
            //int[] A={1,5,3,1,5,9,5,1,3,2,4};
            //int[] A={2, 5, 1, 1, 1, 1, 4, 1, 7, 3, 7};
            Boolean res = resolve3(A);
    
            System.out.println(String.valueOf(res));
    
        }
    }
    
    
    
    
    
    
  • 相关阅读:
    一起谈.NET技术,.Net Discovery系列之深入理解平台机制与性能影响(下) 狼人:
    一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(十二)魔法系统 狼人:
    一起谈.NET技术,.Net Discovery系列之深入理解平台机制与性能影响 (中) 狼人:
    一起谈.NET技术,再次分享一个多选文件上传方案 狼人:
    一起谈.NET技术,Silverlight 2.5D RPG游戏技巧与特效处理:(十一)AI系统 狼人:
    一起谈.NET技术,.NET中的异步编程 IO完成端口以及FileStream.BeginRead 狼人:
    一起谈.NET技术,C#中标准Dispose模式的实现 狼人:
    一起谈.NET技术,DotNet并行计算的使用误区 狼人:
    一起谈.NET技术,.NET中的委托 狼人:
    一起谈.NET技术,ASP.NET MVC3 基础教程 – Web Pages 1.0 狼人:
  • 原文地址:https://www.cnblogs.com/lwhp/p/6505404.html
Copyright © 2020-2023  润新知