程式設計之美上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 #include using namespace std;
int f[100][50][1000];
int p[100][50][1000];
int getsplitarraysum(int *arr, int n)//n代表元素個數不包括第0個
}} }
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<
此方法的空間複雜度為o(2n*n*sum/2)即o(n^2*sum),時間複雜度為o(n2sum)。還可以優化麼?待續~
《程式設計之美》 2 18 陣列分割
題目概述 有乙個沒有排序,元素個數為2n的正整數陣列。要求把它分割為元素個數為n的兩個陣列,並使兩個子陣列的和最接近。假設陣列a 1.2n 所有元素的和是sum。模仿動態規劃解0 1揹包問題的策略,令s k,i 表示前k個元素中任意i個元素的和的集合。顯然 s k,1 s k,k s k,i s k...
程式設計之美 2 18 陣列分割
本人第一次寫部落格,如有不對,請多加指正。解法一的思路很明顯是錯的。貪心演算法很多情況下求不出最佳解答,因為可能兩個陣列間需要同時交換兩個或者兩個以上的數,才能實現差值最小。如 解法二的思路將也就是乙個組合的問題在2n個陣列中找出n個數,使得n個數之和最接近於sum 2,這裡取小於等於sum 2的情...
《程式設計之美》 2 18 陣列分割
題目概述 有乙個沒有排序,元素個數為2n的正整數陣列。要求把它分割為元素個數為n的兩個陣列,並使兩個子陣列的和最接近。假設陣列a 1.2n 所有元素的和是sum。模仿動態規劃解0 1揹包問題的策略,令s k,i 表示前k個元素中任意i個元素的和的集合。顯然 s k,1 s k,k s k,i s k...