1. 程式人生 > >阿里測評題——拔河比賽(動態規劃)

阿里測評題——拔河比賽(動態規劃)

拔河比賽

問題描述:n 個人參加拔河比賽,每個人有自己的重量,現在需要把他們分成兩組進行比賽,每個人屬於其中的一個組。為使比賽公平,求使得兩組重量差最小的分配。

輸入:第一行為一個整數N,表示參加比賽的人數;第二行為N個正整數,分別表示對應人員的體重。
輸出:兩隊體重總量的最小差值。

阿里的測評題很喜歡考動態規劃題,這個題目也一樣。我有看到其他部落格的方法(同樣是動態規劃),時間複雜度為o(n^2),我的方法T(n)=o(n^3)。但是自己的方法自己能看懂,所以這裡也試著寫了一下。

分析:
① 要考察兩隊體重總量的差異最小的問題。只要想到,從N個人中選出一隻隊伍,隊伍的體重總量越接近N個人的體重總量的一半(avg),就越能縮小兩隊間體重總量的差距。兩隊是此消彼長的關係,兩者與avg的差的絕對值是相等的;
② 兩隻隊伍之間人數相差不能超過1。我在程式碼中考慮n為偶數和奇數的情況,隊伍的總人數必須是不能超過n/2+1的(N無論奇偶,都是這個上界)。

通過一個輔助二維陣列dp[][],內部元素為dp[i][j],i大於0,小於等於N/2+1。j大於0,小於等於avg。dp[i][j]表示的是,當隊伍選拔第i名隊員,且隊伍體重總量上限為j時,可以容納的隊員體重總量的最大值。
(之前就沒有想明白揹包問題,導致題目沒做出來。揹包問題中是需要求最大價值,這裡的情況只不過是加了“價值不能超過容量”的限制而已。)

後面的過程可以結合註釋理解。以下是java程式碼:

import java.util.Scanner;

public class AliTest {
    public static void main(String[] args) {
        Scanner sc=new
Scanner(System.in); while(sc.hasNext()){ //接收人數 int n=sc.nextInt(); //如果n是0,直接輸出0; if(n==0){ System.out.print(0); continue; } //用arr接收運動員的體重 int[] arr=new int[n]; //統計所有運動員的總體重,用於計算sum/2
int sum=0; for(int i=0;i<n;i++){ arr[i]=sc.nextInt(); sum+=arr[i]; } //輔助陣列 int[][] dp=new int[n+1][sum/2+1]; //初始化輔助陣列,元素全初始化為0 for(int i=0;i<=n;i++){ for(int j=0;j<=sum/2;j++) dp[i][j]=0; } //外面的兩層迴圈為輔助陣列的索引,i是人數,由於統計的結果存在前後依賴關係 //比如兩個人的結果實際是依賴一個人的結果的。所以i從1開始。 for(int i=1;i<=n/2+1;i++){ //j的值從小變大,或者從大變小感覺都可以的樣子,因為dp[i][j]的求解與i-1層的值相關,遍歷只是為了取極大值。 for(int j=1;j<=sum/2;j++){ //對每一個元素都進行檢查。獲取所有情況。 for(int k=0;k<n;k++) //空間夠大才能裝下arr[k]。空間不足,用dp[i-1][j]遞推過來。 if(j>=arr[k]) //用剩餘空間與待放入的元素比較,空間過剩,直接累加。 if (j - dp[i][j] >= arr[k]) dp[i][j] += arr[k]; //空間不足時,檢查當人數減一個的情況下,有沒有剩餘空間可以容納arr[k]的,有的話,直接累加。然後與當前的值比較。取最大值。 else dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - arr[k]] + arr[k]); //空間不足的情況 else dp[i][j]=dp[i-1][j]; } } //初始化比較的min為最大值 int min=Integer.MAX_VALUE; //偶數時,隊伍人數可以是n/2-1,n/2,n/2+1;奇數時可以為n/2,n/2+1。 if(n%2==0){ //從符合條件的人數列中選出最小值 for(int i=n/2-1;i<=n/2+1;i++){ //最小值指的是隊伍體重總和的差值的,絕對值的,最小值 if((sum-dp[i][sum/2])*2<min) min=sum-dp[i][sum/2]*2; } }else{ for(int i=n/2;i<=n/2+1;i++){ //同上 if((sum-dp[i][sum/2])*2<min) min=sum-dp[i][sum/2]*2; } } //輸出結果 System.out.print(min); } sc.close(); } }

阿里的測評時限為30分鐘。題目刷的太少,導致想出來的時候,已經超時了。。。。而且有要求使用python語言(我申請的是資料研發崗),無法切換成java,導致了最終沒能做出這道題。當時系統直接自動交卷了,我沒來得及拷貝測試用例。我用了以下幾個用例測試,均通過:
① 0
② 5 5 5 5 5 5
③ 6 1 2 3 4 5 6
哪位大神發現了問題,一定記得留言告訴我。感謝!!!