編程之美2.18_由數組分割到揹包問題(1)

編程之美上2.18題:

有一個無序,元素個數爲2n的正整數數組,要求:如何能把這個數組分割爲元素個數爲n的兩個數組,並使兩個子數組的和最接近?

eg:1,5,7,8,9,6,3,11,20,17->1,3,11,8,20     5,7,9,6,17

 

分析:

題目的本質是從2n個整數中找出n個數,使得其和最接近所有整數總和的一半。

編程之美的解法一已經明確了,該種揭發不是最優的。

下面兩種解法主要是用動態規劃,雖然我看的不是很明白,但是這讓我想到了這題可以用揹包問題來求解。揹包問題是標準的動態規劃問題。下面是思路:

 

由題目看,此題應該轉換爲01揹包問題,因爲其中的元素每個只能取一次。揹包的容量W爲sum/2,物品的總個數M=2n。但是01揹包問題只需要求得裝入揹包的物品使得總價值最大即可,對裝入物品的個數並沒有限制。但是這裏不同,裝入揹包的物品總價值不僅要最大,而且裝入的物品個數一定是2n的一半:n個!這就需要好好的思考一下了~

 

我們假設F[i][j][k]表示從前i個數中取j個數,其和不超過k且最接近k的一個值。

那麼利用01揹包的思想:

F[i][j][k] = max(F[i-1][j][k],F[i-1][j][k-A[i]]+A[i]),其中1<=i<=2n,1<=j<=min(i,n),1<=k<=sum/2,前一個式子表示第1個數不取,後一個式子表示取第i個數。這樣,最後取的n個數的總和結果就是F[2n][n][sum/2]。注意,前後兩個式子表示的情況是完備且互斥的

另外有一個很關鍵的是求到底取了數組中的讀幾個數,當F[i][j][k] = F[i-1][j][k-A[i]]+A[i]時,說明第i個數被取,這時候需要一個數組P[][][]進行記錄。可以這麼做:

當F[i][j][k] = F[i-1][j][k-A[i]]+A[i]時,令P[i][j][k] = 1,最後從F[2n][n][sum/2]逆着走向F[0][0][0,],若發現F[i][j][k]= 1, 則說明數組中的第i個數取了,同時k = k-A[i],j = j-1,i = i-1。

好了,直接上代碼:

#include <iostream>
#include <algorithm>
using namespace  std;

int F[100][50][1000];
int P[100][50][1000];

int GetSplitArraySum(int *arr, int n)//n代表元素個數不包括第0個
{
	int sum = 0;
	for (int i=1; i<=n; ++i)
		sum += arr[i];
	int val = sum>>1;

	for (int i=1; i<=n; ++i) {
		for (int j=1; j<=min(i, n>>1); ++j) {
			for (int k=1; k<=val; ++k) {
				F[i][j][k] = F[i-1][j][k]; 
				if (k>=arr[i] && F[i][j][k] < F[i-1][j-1][k-arr[i]]+arr[i])  {
					F[i][j][k]=F[i-1][j-1][k-arr[i]]+arr[i] ;
					P[i][j][k] = 1;
				}
			}
		}
	}


	return F[n][n>>1][val];
}

void PrintElem(int *arr, int n)
{
	int sum = 0;
	for (int i=1; i<=n; ++i)
		sum += arr[i];
	int val = sum>>1;

	int i= n;
	int j = n>>1;
	int k = val;
	while (i>0 && j>0 && k>0) {
		if (P[i][j][k]==1) {
			cout<<arr[i]<<" ";
			--j;
			k -= arr[i];
		} 
		--i;
	}
	cout<<endl;

}
int main(int argc, char **argv)
{
	int arr[10+1] = {0,1,5,7,8,9,6,3,11,20,17};
	cout<<GetSplitArraySum(arr, 10)<<endl;
	PrintElem(arr, 10);

	system("pause");
	return 0;
}

此方法的空間複雜度爲O(2N*N*Sum/2)即O(N^2*Sum),時間複雜度爲O(N2Sum)。還可以優化麼?待續~


發佈了29 篇原創文章 · 獲贊 46 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章