分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!
一、題目概述:有乙個沒有排序,元素個數為2n的正整數陣列。要求把它分割為元素個數為n的兩個陣列,並使兩個子陣列的和最接近。
假設陣列a[1..2n]所有元素的和是sum。模仿動態規劃解0-1揹包問題的策略,令s(k, i)表示前k個元素中任意i個元素的和的集合。顯然:
s(k, 1) =
s(k, k) =
s(k, i) = s(k-1, i) u
按照這個遞推公式來計算,最後找出集合s(2n, n)中與sum/2最接近的那個和,這便是答案。這個演算法的時間複雜度是o(2^n).
因為這個過程中只關注和不大於sum/2的那個子陣列的和。所以集合中重複的和以及大於sum/2的和都是沒有意義的。把這些沒有意義的和剔除掉,剩下的有意義的和的個數最多就是sum/2個。所以,我們不需要記錄s(2n,n)中都有哪些和,只需要從sum/2到1遍歷一次,逐個詢問這個值是不是在s(2n,n)中出現,第乙個出現的值就是答案。我們的程式不需要按照上述遞推公式計算每個集合,只需要為每個集合設乙個標誌陣列,標記sum/2到1這個區間中的哪些值可以被計算出來。關鍵**如下:
#include
using
namespace
std;//有乙個沒有排序,元素個數為2n的正整數陣列。要求把它分割為元素個數為n的兩個陣列,並使兩個子陣列的和最接近。
int arr = ;const
int n=5;const
int sum = 87;// 模仿動態規劃解0-1揹包問題的策略
intsolve1
() dp(2n,n,sum/2+1)就是題目的解。 */
//初始化
memset(dp,0,sizeof(dp)); for(i = 1 ; i <= 2*n ; ++i) } } //因為這為最終答案 dp[2*n][n][sum/2+1]; i=2*n , j=n , s=sum/2+1; while(i > 0) i--; } cout
<
solve2
() } } //要求最優解則 空間不能優化,
return dp[n][sum/2+1];}int
solve3
() } } for(s = sum/2+1 ; s >= 0 ; --s) //要求最優解則空間不能優化
return
0;}int
main
(void)
二、
擴充套件問題: 交換兩個陣列元素使兩個陣列和的差最小
有兩個陣列a、b,大小都為n,陣列元素的值任意整形數,無序;
要求:通過交換a、b陣列中的元素,使[陣列a元素的和]與[陣列b元素的和]之間的差最小。
其實這個問題就是上面問題的變形,將a、b兩個陣列合併為乙個陣列,然後問題就轉化為將2*n個元素陣列分割為2個長度為n的陣列,並使兩個子陣列的和最接近。
另外,特別注意:如果陣列中有負數的話,上面的揹包策略就不能使用了(因為第三重迴圈中的s是作為陣列的下標的,不能出現負數的),需要將陣列中的所有陣列都加上最小的那個負數的絕對值,將陣列中的元素全部都增加一定的範圍,全部轉化為正數,然後再使用上面的揹包策略就可以解決了。
給我老師的人工智慧教程打call!
程式設計師面試100題之十一 陣列迴圈移位
設計乙個演算法,把乙個含有n個元素的陣列迴圈右移k位,要求時間複雜度為o n 且只允許使用兩個附加變數。不合題意的解法如下 我們先試驗簡單的辦法,可以每次將陣列中的元素右移一位,迴圈k次。abcd1234 4abcd123 34abcd12 234abcd1 1234abcd。如下所示 rights...
程式設計師面試100題之十二 求陣列中最長遞增子串行
寫乙個時間複雜度盡可能低的程式,求乙個一維陣列 n個元素 中最長遞增子串行的長度。例如 在序列1,1,2,3,4,5,6,7中,其最長遞增子串行為1,2,4,6。分析與解法 根據題目要求,求一維陣列中的最長遞增子串行,也就是找乙個標號的序列b 0 b 1 b m 0 b 0 解法一根據無後效性的定義...
程式設計師面試100題之十二 求陣列中最長遞增子串行
寫乙個時間複雜度盡可能低的程式,求乙個一維陣列 n個元素 中最長遞增子串行的長度。例如 在序列1,1,2,3,4,5,6,7中,其最長遞增子串行為1,2,4,6。分析與解法 根據題目要求,求一維陣列中的最長遞增子串行,也就是找乙個標號的序列b 0 b 1 b m 0 b 0 解法一根據無後效性的定義...