任意2n個正整數數組,將其分割成兩個長度爲n的數組,使兩子數組之和的差值最小

題目描述:任意2n個正整數數組,將其分割成兩個長度爲n的數組,使兩子數組之和的差值最小。
或者:從2n個正整數中選取n個,使這n個數字之和和剩餘n個數字之和的差值最小。

這道題利用動態規劃進行求解,可以採用0-1揹包問題的策略,放或者不放,不太瞭解0-1揹包的,參考鏈接https://blog.csdn.net/qq_34826261/article/details/100663768。
假設數組的和爲sum,我們的目標可以轉化爲求數組中n個元素之和和最接近sum/2的那個和,大於或者小於關於sum/2對稱,找小於sum/2的最接近sum/2的那個和更加方便。定義f(j,k)爲有j個數字,和爲k的標誌,即對長爲2n的數組進行遍歷時,是否可將當前數字放入使其構成長爲j,和爲k的分組,是則爲true,也就是說最後得到的結果在f(n,k)=true(k<=sum/2且最接近sum/2)中,其中一個分組的和爲:
(1)k=max{f(n,k)=truek&lt;=sum/2}k=max\{f(n,k)=true|k&lt;=sum/2\}\tag{1}
先放出來更新公式:
(2)f(j,k)=true,if(k&gt;=A[i]&amp;&amp;f(j1,kA[i])=true),i&lt;=2nf(j,k)=true,if(k&gt;=A[i]\&amp;\&amp;f(j-1,k-A[i])=true),i&lt;=2n\tag{2}
對更新公式解釋,對於任意長度的子數組的和有以下幾種情況,h(i,j)表示從長i的數組中取j個元素之和的集合,
(3)h(i,0)={0}h(i,1){A[m]m&lt;=i}h(i,j)={h(i1,j1)+A[i]j&lt;i}{h(i1,j)j&lt;i}h(i,i)={A[0]+A[1]+...+A[i]}h(i,0) = \{0\}\\ h(i,1) ∈ \{A[m]|m&lt;=i\}\\ h(i,j) = \{h(i-1,j-1)+A[i]|j&lt;i\}∪\{h(i-1,j)|j&lt;i\}\tag{3}\\ h(i,i) = \{A[0]+A[1]+...+A[i]\}
如果對於任意的j,h(i,j)中存在k,則f(j,k) = true。對於任意f(j,k)=true,則k∈h(i,j),則必有k+A[m]∈h(i,j+1),m<i,則f(j,k+A[m]) = true,反過來,若k∈h(i,j),則必存在k-A[m]∈h(i,j-1),本段新加入的A[m]都和已有的j個元素不重複。
下面考慮(2)式,假設我們已經獲取了i-1時的f(j,k),則i時如何更新f(j,k),不難發現從i-1到i僅僅多出了A[i]這一個數字,相當於(3)式的第三個式子等號右邊的第一項,這時候若h(i-1,j-1)中存在k-A[i],即f(j-1,k-A[i])=true,則f(j,k) = true。
代碼如下:

public int minDValueofN(int[] nums) {
	int sum = 0;
	int len = nums.length / 2;
	for(int i = 0; i < nums.length; i++) {
		sum += nums[i];
	}
	boolean[][] flag = new boolean[len+1][sum/2+1];
	flag[0][0] = true;
	for(int i = 1; i <= nums.length; i++) {
		// 注意j是逆序更新
		// 這裏是因爲更新flag[j][]需要使用flag[j-1][],後面的更新不會影響前面的值
		// 實際上可以做一個三維的動態規劃flag[i][j][k] = flag[i-1][j-1][k-nums[i-1]]
		// 但是沒有必要,可以參考一下01揹包問題的優化
		// https://blog.csdn.net/qq_34826261/article/details/100663768
		for(int j = i<=len?i:len; j > 0; j--) {
			for(int k = 0; k <= sum/2; k++) {
				if(k >= nums[i-1] && flag[j-1][k-nums[i-1]]) {
					flag[j][k] = true;
				}
			}
		}
	}
	// 找出最接近sum/2的k值
	for(int k = sum/2; k > 0; k--) {
		if(flag[len][k]) {
			return Math.abs(2*k - sum);
		}
	}
	return -1;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章