BAT實習內推筆試卷(第一場)——我的答案以及分析

第一題:

給定一個長度不小於2的數組arr。java

寫一個函數調整arr,使arr中要麼所有的偶數位上都是偶數,要麼所有的奇數位上都是奇數上。 要求:假設數組長度爲N。時間複雜度請達到O(N),額外空間複雜度請達到O(1),下標0,2,4,6...算做偶數位,下標1,3,5,7...算做奇數位,好比[1,2,3,4]調整爲[2,1,4,3]就能夠
數組


分析:函數

時間複雜度請達到O(N),就不能用多重循環。額外空間複雜度請達到O(1),,也就不能用輔助的數組之類的,就是要在單次掃描中,經過交換2個數的位置來實現。post

事實上咱們換個思惟來想,假設有2個一樣長度數組A,B。知足要麼A數組全是偶數。要麼B數組全是奇數,是否是就能很是快的有思路了。想一下再看如下分析。spa






2個指針P_A和P_B分別指向A,B,首先P_A找出第一個奇數,而後P_B找出第一個偶數,交換。而後循環這個方案,直到某個指針達到數組的末尾。指針

事實上這個題和上面的思路同樣。至關於把2個數組交叉放在一塊兒。偶數位置的至關於A數組。奇數位置至關於B數組。僅僅是每次移動2個位置code


代碼:orm

public class Solution {
    /**
     *  奇數位上都是奇數或者偶數位上都是偶數
     *  輸入:數組arr。長度大於2
     *  將arr調整成奇數位上都是奇數或者偶數位上都是偶數
     */
        public void oddInOddEvenInEven(int[] arr) {
        int len = arr.length;
        if (len <= 2) {
            return;
        }
        int even = 0;
        int odd = 1;
        while (even < len && odd < len) {
            if (arr[even] % 2 != 0)
            {
                odd = findEven(arr, odd);
                if (odd < len) {
                    int temp = arr[odd];
                    arr[odd] = arr[even];
                    arr[even] = temp;
 
                }
            }
            even += 2;
        }
 
 
    }
 
    private int findEven(int[] arr, int odd) {
        for (int i = odd; i < arr.length; i+=2) {
            if (arr[i] % 2 == 0) {
                return i;
            }
        }
        return arr.length;
    }
}


第二題 

給定一個全是正數的數組arr。定義一下arr的最小不可組成和的概念: 1。arr的所有非空子集中,把每個子集內的所有元素加起來會出現很是多的值。當中最小的記爲min,最大的記爲max; 2。在區間[min,max]上。假設有一些正數不可以被arr某一個子集相加獲得。那麼這些正數中最小的那個,就是arr的最小不可組成和; 3。在區間[min,max]上。假設所有的數都可以被arr的某一個子集相加獲得,那麼max+1是arr的最小不可組成和; 舉例: arr = {3,2,5} arr的min爲2,max爲10,在區間[2,10]上。4是不能被不論什麼一個子集相加獲得的值中最小的,因此4是arr的最小不可組成和; arr = {3,2,4} arr的min爲2,max爲9。在區間[2,9]上。8是不能被不論什麼一個子集相加獲得的值中最小的,因此8是arr的最小不可組成和; arr = {3,1,2} arr的min爲1。max爲6,在區間[2,6]上,不論什麼數都可以被某一個子集相加獲得,因此7是arr的最小不可組成和; 請寫函數返回arr的最小不可組成和。

分析:
我我的的思路是這樣
假設僅僅有一個元素。那麼確定是arr[0]+1
假設有2個元素,假設a1<=a2  假設a1+1=a2 那麼確定是a2+1, 不然確定是a1+1
3個元素以上,若是排序後的順序爲 a1<=a2<=a3<=a4...<=ai,先取前面2個元素a1和a2,a1和a2能組合成的所有狀況。就可以利用一個輔助集S合記錄,首先集合裏面僅僅有3個元素(a1,a2,a1+a2), 而後考慮第3個元素a3,那麼a1,a2,a3,a4能組合的所有狀況是什麼呢:
a1
a2
a3
a1+a2
a1+a3
a2+a3
a1+a2+a3
事實上可以看出規律,與輔助集合S中的元素相比,多了的a3,a1+a3,a2+a3,a1+a2+a3,除了a3,其它是否是就是原有集合S中的元素分別與s3相加呢?
原理是這樣,輔助集合S的做用就是保存前面所有可能出現的,經過相加能獲得數,這樣再與新的元素分別相加。就能獲得當前所有的組合。

要找出最小的,不用找出所有的不可組成和。當咱們新增長一個元素ai的時候。僅僅用推斷a1到ai之間的數,是否是都在輔助集合S中,第一次不可組成和,就是最小不可組成和,因爲,ai以後的元素都不會比ai小。
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Solution {
    /**
     *  正數數組中的最小不可組成和
     *  輸入:正數數組arr
     *  返回:正數數組中的最小不可組成和
     */
    public int getFirstUnFormedNum(int[] arr) {
        int len=arr.length;
        if(len==0){
            return 0;
        }
        if(len==1){
            return arr[0]+1;
        }
        Arrays.sort(arr);
        if(len==2){
            return arr[1]-arr[0]==1?

arr[1]+1:arr[0]+1; } int min=arr[0]; int max=arr[len-1]; int sum=0; for(int i=0;i<arr.length;i++){ sum+=arr[i]; } int[] flag=new int[sum-min+1]; Set<Integer> integers=new HashSet<Integer>(); integers.add(arr[0]); integers.add(arr[1]); integers.add(arr[1]+arr[0]); int set_max=arr[1]+arr[0]; setFlag(arr[0],min,flag); setFlag(arr[1],min,flag); setFlag(set_max,min,flag); for(int i=2;i<len;i++){ int temp=arr[i]; Set<Integer> temp_set=new HashSet<Integer>(integers); temp_set.add(temp); setFlag(temp,min,flag); for (Iterator iterator = integers.iterator(); iterator.hasNext();) { int v = (Integer) iterator.next(); temp_set.add(v+temp); setFlag(v+temp,min,flag); } set_max+=temp; integers=temp_set; int res=judge(flag,temp-min); if(res!=-1){ return res+min; } } int res=judge(flag,sum-min); if(res!=-1){ return res+min; } return sum+1; } private int judge(int[] flag, int max) { for(int i=0;i<max;i++){ if(flag[i]==0){ return i; } } return -1; } private void setFlag(int i, int min, int[] flag) { flag[i-min]=1; } }排序



第三題

面值爲正數的硬幣放置成一排。玩家1和玩家2輪流拿走硬幣。規定每個玩家在拿硬幣時,僅僅能拿走最左或最右的硬幣。 好比: 硬幣面值與排列爲:1,2,3,4,5,現在輪到玩家1拿硬幣。 在當前狀態下,玩家1僅僅能拿走1或5。 假設玩家1拿走1。則排列變爲2,3,4,5。那麼接下來玩家2可以拿走2或5。而後繼續輪到玩家1拿硬幣... 假設玩家1拿走5。則排列變爲1,2,3,4,那麼接下來玩家2可以拿走1或4;而後繼續輪到玩家1拿硬幣... 遊戲依照這個規則進行,直到所有的硬幣被拿完。每個玩家得到的分數是各自拿走硬幣的總和。

遊戲勝負的規定: 假設玩家1最後得到的分數大於玩家2,則玩家1獲勝; 假設玩家2最後得到的分數大於玩家1,則玩家2獲勝; 因爲玩家1先拿硬幣。因此假設最後兩人得到分數同樣則玩家2獲勝; 給定一個數組arr。表示硬幣的面值和排列情況,請返回終於獲勝者的分數。遊戲

樣例: arr=[8,7,5,3] 玩家1將獲勝,分數爲13 因此返回13 arr=[1,9,1] 玩家2將獲勝,分數爲9 因此返回9


分析,事實上就是從動態規劃。用一個動態數組d。d[i][j]=max(arr[i]-d[i+1][j],arr[j]-d[i][j-1]) i<j
事實上d[i][j]就是表示。假設先取的人。會比後取的人,多多少分?負值表示少多少分。
那麼每次比較取左邊和取右邊,找出最優的
至於return那個公式。事實上是這樣來的。x+y=sum ,x-y=d    -->   x=(sum+d)/2

public class Solution {
    /**
     *  獲得硬幣博弈問題的獲勝分值
     *  輸入:表明硬幣排列狀況的數組arr
     *  返回:硬幣博弈問題的獲勝分值
     */
    public int getWinValue(int[] arr) {
        int len=arr.length;
        int[][] d=new int[len][len];
        int sum=0;
        for(int i=0;i<len;i++){
            d[i][i]=arr[i];
            sum+=arr[i];
        }
        for(int w=1;w<len;w++){
            for(int i=0;i<len-w;i++) {
                rule(arr,i,i+w,d);
            }
        }
        return (Math.abs(d[0][len-1])+sum)/2;
 
    }
 
    private void rule(int[] arr, int i, int w, int[][] d) {
 
 
 
            int left=arr[i]-d[i+1][w];
            int right=arr[w]-d[i][w-1];
            if(left>right){
                d[i][w]=left;
            }else{
                d[i][w]=right;
            }
 
    }
}
相關文章
相關標籤/搜索