阿里測評題——拔河比賽(動態規劃)
拔河比賽
問題描述: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
哪位大神發現了問題,一定記得留言告訴我。感謝!!!