阿里在線編程,去除三個元素,四等分數組問題!








/**
 * 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));

    }
}






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM