引論:這是第二次在算法課上的上機實踐了,雖然逐漸有了思考算法的感受,但這一次上機實踐仍是並不輕鬆。關鍵在於在實驗課前對第三章的知識掌握得還不足夠,常常會由於明明知道理論上如何解題而苦於沒法將其實現。不過,經過本次上機實踐,個人收穫是很是豐富的,下面用實踐課題目中的第一題《數字三角形》開始分享個人經驗。ios
給定一個由 n行數字組成的數字三角形以下圖所示。試設計一個算法,計算出從三角形的頂至底的一條路徑(每一步可沿左斜線向下或右斜線向下),使該路徑通過的數字總和最大。算法
輸入有n+1行:編程
第 1 行是數字三角形的行數 n,1<=n<=100。數組
接下來 n行是數字三角形各行中的數字。全部數字在0-99 之間。函數
輸出最大路徑的值。flex
在這裏給出一組輸入。例如:spa
5 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5
在這裏給出相應的輸出。例如:30
設計
初看這一道題的時候,差點陷入到了迷惑之中,不過簡單分析一下題目後發現是那樣的熟悉——題目要求咱們從數字三角形的最頂端數字開始,選取一條可以使通過的數字總和最大的路徑。而這不就和老師在第三章開頭課說的例子同樣嗎?從A地到D地選取一條通過B、C兩地的最短路徑。A到D的最短路徑取決於B到D,而B到D取決於C到D。咱們將這種思想反推至這道題目,要求出數字三角形的最短路徑,從頂部數字開始到底部數字,這要先取決於頂部數字到第二行的最大和,再取決於第二行數字到第三行的最大和,以此類推至數字三角形的最後一行。好的,有了這種動態規劃的思想咱們離解決問題還有一半距離了!code
那麼咱們開始分析,由於每一行的最大和都取決於上一行的數字最大和,那麼,咱們自底而上分析,重填輸入進來的二維數組。重寫時,數字三角形的最後一行是不變的,由於最後一行的數字們沒有下面一行的數字影響,而從倒數第二行開始,這些數字重寫後變爲,它自身與和它相鄰的最後一行兩個數字之和的最大值。舉個例子,即a[i][j]的大小是max{(a[i+1][j] + a[i][j]), (a[i+1][j+1])}。那麼咱們自下而上重寫數組,最後獲得新的最頂端的數字,便是最大值路徑。blog
首先列出我解題的代碼
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 int main() 6 { 7 int num; 8 cin>>num; 9 const int n = num; 10 int array[n][n]; 11 12 for (int i = 0; i < n; i++) 13 { 14 for (int j = 0; j <= i; j++) //按數字三角型的形式,填充二維數組(矩陣)的下三角 15 { 16 cin>>array[i][j]; 17 } 18 } 19 20 //用動態規劃的思想,改寫array二維數組 21 for (int i = n - 2; i >= 0; i--) //由於數字三角形的最後一行的動態規劃最大值是本身,因此從倒數第二行開始求自下而上時的動態規劃最大值 22 { 23 for (int j = 0; j < i + 1; j++) 24 { 25 array[i][j] = max((array[i + 1][j] + array[i][j]), (array[i + 1][j + 1] + array[i][j])); //當前元素的動態規劃最大值是判斷它自己和它左下邊或右下邊的數字相加的最大值 26 } 27 } 28 cout<<array[0][0]; //由於是自下而上求最大值,因此二維數組第一個元素便是數字三角形的最大值 29 return 0; 30 }
由於改寫後的數組,數字三角形的最後一行是不變的,因此咱們的第一層循環從倒數第二行開始,所以定義i = n - 2,並讓i不斷變小;在第二層循環中,咱們分析的是當前行的每一個數字,每一行的數字數目爲該行行號,而i爲每一行的下標,因此定義j = i + 1。而咱們填入數組的是該行數字和與它相鄰的下一行數字組成之和的最大值。最終循環結束後,咱們直接輸出數組的第一個數字,即獲得最大路徑和。
對於時間複雜度:本算法爲main函數中的重疊的兩個for循環,而第一層循環是從n-2開始至0,第二層循環爲從0開始至n-1,所以不可貴出,本算法的時間複雜度爲O(n^2)。
對於空間複雜度:由於本題是直接改寫輸入進來二維數組,因此沒有額外申請數組和內存空間,因此空間複雜度爲O(1)。
經過本次上機實踐,我更好地鞏固了第三章的知識,動態規劃思想理解起來不難,但須要屢次的動手實踐來讓本身真正掌握它。經過本身動手編程解決這一道題讓我加深了上節課所學知識的印象。在想出上述解題算法以前,我在草稿本上列出了不少種填表的方式,但難以實現出來,即便解決了這一道題,我目前對填表法的掌握還並不熟練。不過,經過作這一道實驗題,我真正明白了動態規劃思想的厲害之處。由於實驗題目集中的第二題是書上的原題,因此相對於第二題,我在第一題花費的時間較多。所以,在解決動態規劃思想的題目中,咱們不能糾結於如何寫出完完整整的遞推式,而要多結合題目實際。
同時,解決這道題目也不只僅是我一我的想出來的,這道題目的核心思想是個人搭檔爲我講解的,所以光靠本身一我的我是沒法解決這道題目的。這又再次體現出結對編程的重要性,兩我的的頭腦風暴得出來的結果是實驗課中最好的成果。
經過本次實驗,我但願本身可以再接再礪,不斷鑽研算法知識,爭取解決更多的問題!