《演算法導論》筆記(9) 動態規劃 部分習題

2021-06-28 16:06:50 字數 3557 閱讀 9296

一些本章的習題的解法簡答及偽碼。

15.1-5 斐波那契數的子問題圖。每個斐波那契數與前兩個斐波那契數有關。所以有n個頂點,2*(n-2)條邊。

15.2-4 矩陣鏈乘法的子問題圖。n個矩陣的子問題是n-1對,所以共有(n-1)*n/2與n*(n-1)*(n-2)/2條邊。

15.3-6 外幣兌換問題。必須是不重複的兌換。可以做乙個集合s=代表兌換序列,sn=r12*r23*…*rn代表最終兌換金額,那麼開始是,然後每次加入乙個新元素i,找到最佳的si。可以證明新加入的元素不改變原來序列的順序,只需要找乙個最佳位置插入。那麼每次的子問題就是for(i=2 to n) find_best_sn。

15.4-5。最長單調子串行。若已知到第i位為止的最長單調子串行,考察第i+1位a[i+1],會發生什麼情況?它能夠續接到到那些末位s[j])

} }}

最差執行時間是o(n^2)。最長單調子串行的元素個數是a[n]即a的最後一項,b[i]是第i位作為最長單調子串行的最後一項時,其前驅項在s的位置。最長單調子串行是l:

longest_s(b)

return l;

}

15.4-6 構造乙個陣列a,a[i]表示長度為i的最長單調子串行的最後一項的最小值。s掃瞄到第s[j]項,找到s[j]是在a中的位置i並替換,此時s[j]是構成長度為i的最長單調子串行的最後一項最小值。因為s[j]是已知的i長度最長單調子串行的最後一項,所以插入不會影響已知的長度大於i的最長單調子串行,即a中排在i之後的所有項。

longest(s)

else

a[j]=s[i]; b[i]=j;

}}}

最差執行時間是o(n*lg(n))。s掃瞄一遍後,a的長度即是s的最長單調子串行。每次在a中替換s的第i個元素s[i]時,s[i]插入a[j]的位置j記錄在b[i]中,那麼b中最大值b[i]表明了最長單調子串行的最後一項是s[i],依次倒推,b[i]左側與其最靠近的次大的b[k]值代表最長單調子串行的倒數第二項是s[k]。最長單調子串行是l:

longest_s(b)  } 

return l;

}

思考題 15-1 有向無環圖的最長簡單路徑。維持一張二維的**r,乙個集合s,rij表明i到j點的最長路徑,s表明已搜尋的點。搜尋與t點直接相連的所有點,這些點記為s1,在r中記錄下相關的rij值,並將s1加入到s。檢查s1有無互相連線,如果有,更新最大路徑rij值。然後依次搜尋每個與s1點直接相連的點s2,記錄所有的s2到s1以及s2到t的路徑rij。檢查s2有無互相連線,如果有,更新最大路徑rij值。下一步是與s2點直接相連的s3,此時相同方法檢查s3到t的路徑,並試圖找到有無因s3內部相連致使最大路徑更新。如此一步一步將所有點加入s後,可以得到s到t的最長簡單路徑。

search_longest(s)

}

思考題15-2 最長回文就是正序與逆序的最長公共子串行。

思考題15-3 雙調歐幾里德旅行商問題。首先確定最左點。然後定義子問題。記i是所有點按照x座標從左到右排序後的序號,取任意不同兩個點i, j,d(i, j)表示從i向左到頭轉身再到j的最短距離。那麼當j

for(i=1 to n)

for(j= 2 to i-1)

update distance(i, j): if j

for layer= n to 1:

update v[node].true, v[node].false; //true, false分別是node的根結點出席與不出席情況的社交值

max(v[root].true, v[node].false)

思考題15-8 構造一張m*n的**d。從第一行開始,d[1, j]表示刪除a[1, j]的破壞度,d[i, j]表示到執行到第i行i列刪除a[i, j]後的最小破壞度。那麼d[i, j]=d[i, j]+min(d[i-1, j-1], d[i-1, j], d[i-1, j+1]),從第1行執行到第n行,找到d[n]中的最小值。

for(i= 1 to m)

for(j= 1 to n)

d[i, j]=d[i, j]+min(d[i-1, j-1], d[i-1, j], d[i-1, j+1]);

best_result=min(all elements in d[m])

思考題15-9 類似最優二叉搜尋樹問題。l[1...m]如果是亂序則排序,在此假設是已經排序的。分解過程類似二叉搜尋樹,分解一次變成2棵子樹。則總花費時間為2棵子樹各自的時間加上此次分解前的元素個數。c[i, j]是i到j之間最佳分解方案。則c[i, j]=min(c[i,r]+c[r+1,j])+(j-i+1),其中min(c[i,r]+c[r+1,j])表示分割點r的各種不同取法,前後兩個子問題解的和的最小值。

for i=1 to m

for j= 1 to i+1

for r= i to j-i+1

c[i, j]=min( c[i, r]+c[r+1, j] , c[i, j] ) + (j-i+1)

best result=c[1, m]

思考題15-10 首先,第i年的結束,上年總收益為d[i]=sum(r[i,1]*d[i,1]+r[i,2]*d[i,2]+...+r[i,j]*d[i,j]),則第i+1年的最佳配置是d[i+1]=sum(r[i+1,1]*d[i+1,1]+r[i+1,2]*d[i+1,2]+..+r[i+1,j]*d[i+1,j])-f2<=max(r[i])*d[i]-f或者d[i]-f1,當i=1時d[1]=max(r[1])*d0,可知每年的最佳策略是將所有的錢投入到單一投資中。且具有最有子結構性質。

具體演算法是構造乙個10*n**,從第一年開始計算d[1, j]=d0*r[1, j],然後計算第二年d[2, j] = max(d[0])*r[1, j]-f2 或 d[i, j]-f1,依次上推。

for i= 1 to 10

for j = 1 to n

a[i, j]= max(a[i-1]) is in j colum? max(a[i-1])*r[i, j]- f2 : a[i-1, j]*r[i, j]- f1;

best_result= max(a[10])

思考題15-12 簽約棒球自由球員問題。其實是0-1揹包問題的變型。遍歷第i個位置的球員,此球員是否加入集合中替換現有相同位置要根據此球員**和vorp,比較已知的相同總價下的最大總vorp來進行決策。其子問題是此位置之前的各種預算的最佳解。遍歷所有球員和位置,可以得到全域性最大v值的解。

for i= 1 to n

for i=1 to p

for each player at position i: s.add_player; v[i, k]= max( player.vorp + v[ i-1, (k-player.price) ], v[i, k] ) //update best v[i]

best_result=v[n]

《演算法導論》筆記 動態規劃概覽

什麼問題適合使用動態規劃來解決?如果乙個問題具有以下兩個特徵,那麼就可能適合用動態規劃來解決 如果乙個問題的最優解包含著該問題的子問題的最優解,那麼這個問題就具有最優子結構。擁有最優子結構特徵的問題,就有可能用動態規劃來解決 當然,也有可能用貪婪演算法 在遞迴式中,乙個問題的子問題,以及子子問題的解...

演算法導論 動態規劃

動態規劃這個演算法,我一直都搞不明白,也許因為我數學能力太差的緣故,總是不得其要領,每次學習這個演算法的時候,總是不知道所謂的狀態轉移方程到底是怎麼樣推導出來的。其實就在我寫這篇部落格的時候,我依然不清楚。什麼問題能用動態規劃來解決呢?動態規劃問題的特徵就是最優子結構,乙個遞迴結構 該問題需要求乙個...

演算法導論 讀書筆記 動態規劃

一.動態規劃介紹 動態規劃與分治法整體思路相近 組合子問題的解求解原問題。將問題劃分為互不相交的子問題,遞迴地求解子問題,再將它們的解組合起來,求出原問題的解。動態規劃的應用場景 子問題重疊。子問題的解決需要解決子子問題 遞迴 而不同子問題之間的子子問題是相同的。動態規劃的實現及特點 對於分治演算法...