演算法(一) --DP動態規劃(LIS和LCS)
1.http://blog.csdn.net/u013445530/article/details/45645307
DP問題是ACM裡面最難的,因為太考思維能力了,只有將狀態轉移方程推出來才能解決問題,DP問題也是面試的時候最容易考到的,希望大家好好學DP,至少在面試的時候不吃虧。
- 第一個問題
d(i)=j來表示湊夠i元最少需要j個硬幣
- int main() //程式碼求解的是一個數值 最少由多少個硬幣組成。
- {
-
int a[3] = {1,3,5},sum = 11,cent = 0,dp[12];
- dp[0] = 0;
- for(int i = 1; i <= sum; i++) dp[i] = i;//我們假設存在1元的硬幣那麼i元最多隻需要i枚1元硬幣,當然最好設定dp[i]等於無窮大
- //dp[i]=i 是代表 最多的話需要i個一元硬幣
- for(int i = 1; i <= sum; i++){ / /i代表數值
- for(int j = 0; j < 3; j++){ //j代表總共的硬幣種類數目 i代表要求和的數
-
if
- dp[i] = dp[i- a[j] ] + 1; //注意這個是從小的數值往大的數值上 增加。
- } //所以 如果i的數值大於A【j】裡面的數 以及dp[i - a[j]] + 1 選個硬幣值a【j】所以要加1
- }
- }
-
cout<<dp[sum]<<endl;
- return 0;
- }
第二個問題
將一個由N行數字組成的三角形,如圖所以,設計一個演算法,計算出三角形的由頂至底的一條路徑,使該路徑經過的數字總和最大。
好了,現在我們用DP解決這道問題
將上圖轉化一下:
假設上圖用map[][]陣列儲存。
令f[i][j]表示從頂點(1, 1)到頂點(i, j)的最大值。
則可以得到狀態轉移方程:
f[i][j] = max(f[i+1][j], f[i+1][j+1]) + map[i][j]
此題既適合自頂而下的方法做,也適合自底而上的方法,
當用自頂而下的方法做時,最後要在在最後一列數中找出最大值,
而用自底而上的方法做時,f[1][1]即為最大值。
所以我們將圖2根據狀態轉移方程可以得到圖3:
這裡用的是從底端向上計算
最大值是30.
程式碼如下:
- 1 #include <cstdio>
- 2. #include <iostream>
- 3. #include <algorithm>
- 4. #include <cstring>
- 5. usingnamespace std;
- 6. int a[2000][2000];
- 7. int main()
- 8. {
- 9. int t,n,i,j;
- 10. while(~scanf("%d",&n))
- 11. {
- 12. for(i=0; i<n; i++)
- 13. for(j=0; j<=i; j++)
- 14. scanf("%d",&a[i][j]); //先把數存進去
- 15. for(i=n-1; i>0; i--) //因為是從底部往上面求 所以i從n-1 開始 從倒數第二行開始算起
- 16. for(j=0; j<i; j++) //注意這裡j的範圍 j從每行的第一個數算起 算到每行最後一個數,最後一個數比i小1
- 17. a[i-1][j]+=max(a[i][j],a[i][j+1]); // 讓此位置的值加上 下面兩個最大的值 即為最大
- 18. printf("%d\n",a[0][0]);
- 19. }
- 20. return 0;
- 21. }
問題三
一個序列有N個數:A[1],A[2],…,A[N],求出最長非降子序列的長度。 (講DP基本都會講到的一個問題LIS:longest increasing subsequence)
正如上面我們講的,面對這樣一個問題,我們首先要定義一個“狀態”來代表它的子問題,並且找到它的解。注意,大部分情況下,某個狀態只與它前面出現的狀態有關,而獨立於後面的狀態。
讓我們沿用“入門”一節裡那道簡單題的思路來一步步找到“狀態”和“狀態轉移方程”。假如我們考慮求A[1],A[2],…,A[i]的最長非降子序列的長度,其中i<N,那麼上面的問題變成了原問題的一個子問題(問題規模變小了,你可以讓i=1,2,3等來分析) 然後我們定義d(i),表示前i個數中以A[i]結尾的最長非降子序列的長度。OK,對照“入門”中的簡單題,你應該可以估計到這個d(i)就是我們要找的狀態。如果我們把d(1)到d(N)都計算出來,那麼最終我們要找的答案就是這裡面最大的那個。狀態找到了,下一步找出狀態轉移方程。
為了方便理解我們是如何找到狀態轉移方程的,我先把下面的例子提到前面來講。如果我們要求的這N個數的序列是:
5,3,4,8,6,7
根據上面找到的狀態,我們可以得到:(下文的最長非降子序列都用LIS表示)
· 前1個數的LIS長度d(1)=1(序列:5)
· 前2個數的LIS長度d(2)=1(序列:3;3前面沒有比3小的)
· 前3個數的LIS長度d(3)=2(序列:3,4;4前面有個比它小的3,所以d(3)=d(2)+1)
· 前4個數的LIS長度d(4)=3(序列:3,4,8;8前面比它小的有3個數,所以 d(4)=max{d(1),d(2),d(3)}+1=3)
OK,分析到這,我覺得狀態轉移方程已經很明顯了,如果我們已經求出了d(1)到d(i-1),那麼d(i)可以用下面的狀態轉移方程得到:
d(i) = max{1, d(j)+1},其中j<i,A[j]<=A[i]
用大白話解釋就是,想要求d(i),就把i前面的各個子序列中,最後一個數不大於A[i]的序列長度加1,然後取出最大的長度即為d(i)。當然了,有可能i前面的各個子序列中最後一個數都大於A[i],那麼d(i)=1,即它自身成為一個長度為1的子序列。
分析完了,上圖:(第二列表示前i個數中LIS的長度,第三列表示,LIS中到達當前這個數的上一個數的下標,根據這個可以求出LIS序列)
程式碼:
1. #include <cstdio>
2. #include <iostream>
3. #include <algorithm>
4. #include <cstring>
5. usingnamespace std;
6.
7. int main() //最長非降子序列的長度
8. {
9. int dp[2000],a[2000],n;
10. while(cin>>n)
11. {
12. memset(dp,0,sizeof(dp));
13. int res = 0;
14. for(inti = 0; i < n; i++) cin>>a[i]; //先存入序列
15.
16. for(int i = 0; i < n; i++)
17. {
//迴圈是從第一個數值開始往後求 dp[i]
18. dp[i] = 1; // 先假設所有的 最長為1 即為其本身 eg:用3 5 4 去想一遍流程
19. for(int j = 0; j < i; j++) //j小於i j為i前面的一個數據
20. {
21. if(a[j] < a[i]) //因為求的是最長非降子序列 LIS,即序列往後只能增加數
//這裡每次都從0開始 遍歷 保證dp[i]為最長的
22. dp[i] = max(dp[i],dp[j] + 1); // 求出的序列 前面的數只能小於或者等於後面的數
23. }
24.
25. res = max(res,dp[i]); //res存放每次 算出來的最大 dp[i]
26. }
27.
28. cout<<res<<endl;
29. }
30. return0;
31. }
問題四
最長上升公共子序列問題: LCS (最長公共子序列) 只要兩個字串裡面都有 即可。數字不必相鄰
問題描述
什麼是最長公共子序列呢?好比一個數列S,如果分別是兩個或多個已知數列的子序列,且是所有符合此條件序列中最長的,則S 稱為已知序列的最長公共子序列。
舉個例子,如:有兩條隨機序列,如 1 3 4 5 5 ,and 2 4 5 6 5 7 6,則它們的最長公共子序列便是:4 5 5。
LCS問題的解決思路
動態規劃演算法
事實上,最長公共子序列問題也有最優子結構性質。
記:
Xi=﹤x1,⋯,xi﹥即X序列的前i個字元 (1≤i≤m)(字首)
Yj=﹤y1,⋯,yj﹥即Y序列的前j個字元 (1≤j≤n)(字首)
假定Z=﹤z1,⋯,zk﹥∈LCS(X , Y)。
·
若xm=yn(最後一個字元相同),則不難用反證法證明:該字元必是X與Y的任一最長公共子序列Z(設長度為k)的最後一個字元,即有zk = xm = yn 且顯然有Zk-1∈LCS(Xm-1 , Yn-1)即Z的字首Zk-1是Xm-1與Yn-1的最長公共子序列。此時,問題化歸成求Xm-1與Yn-1的LCS(LCS(X , Y)的長度等於LCS(Xm-1 , Yn-1)的長度加1)。
·
·
若xm≠yn,則亦不難用反證法證明:要麼Z∈LCS(Xm-1, Y),要麼Z∈LCS(X , Yn-1)。由於zk≠xm與zk≠yn其中至少有一個必成立,若zk≠xm則有Z∈LCS(Xm-1 , Y),類似的,若zk≠yn 則有Z∈LCS(X , Yn-1)。此時,問題化歸成求Xm-1與Y的LCS及X與Yn-1的LCS。LCS(X , Y)的長度為:max{LCS(Xm-1 , Y)的長度, LCS(X , Yn-1)的長度}。
·
由於上述當xm≠yn的情況中,求LCS(Xm-1 , Y)的長度與LCS(X , Yn-1)的長度,這兩個問題不是相互獨立的:兩者都需要求LCS(Xm-1,Yn-1)的長度。另外兩個序列的LCS中包含了兩個序列的字首的LCS,故問題具有最優子結構性質考慮用動態規劃法。
也就是說,解決這個LCS問題,你要求三個方面的東西:1、LCS(Xm-1,Yn-1)+1;2、LCS(Xm-1,Y),LCS(X,Yn-1);3、max{LCS(Xm-1,Y),LCS(X,Yn-1)}。
行文至此,其實對這個LCS的動態規劃解法已敘述殆盡,不過,為了成書的某種必要性,下面,我試著再多加詳細闡述這個問題。
第三節、動態規劃演算法解LCS問題
3.1、最長公共子序列的結構
最長公共子序列的結構有如下表示:
設序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的一個最長公共子序列Z=<z1, z2, …, zk>,則:
1. 若xm=yn,則zk=xm=yn且Zk-1是Xm-1和Yn-1的最長公共子序列;
2. 若xm≠yn且zk≠xm ,則Z是Xm-1和Y的最長公共子序列;
3. 若xm≠yn且zk≠yn ,則Z是X和Yn-1的最長公共子序列。
其中Xm-1=<x1, x2, …, xm-1>,Yn-1=<y1, y2, …, yn-1>,Zk-1=<z1, z2, …, zk-1>。
3、2.子問題的遞迴結構
由最長公共子序列問題的最優子結構性質可知,要找出X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>的最長公共子序列,可按以下方式遞迴地進行:當xm=yn時,找出Xm-1和Yn-1的最長公共子序列,然後在其尾部加上xm(=yn)即可得X和Y的一個最長公共子序列。當xm≠yn時,必須解兩個子問題,即找出Xm-1和Y的一個最長公共子序列及X和Yn-1的一個最長公共子序列。這兩個公共子序列中較長者即為X和Y的一個最長公共子序列。
由此遞迴結構容易看到最長公共子序列問題具有子問題重疊性質。例如,在計算X和Y的最長公共子序列時,可能要計算出X和Yn-1及Xm-1和Y的最長公共子序列。而這兩個子問題都包含一個公共子問題,即計算Xm-1和Yn-1的最長公共子序列。
與矩陣連乘積最優計算次序問題類似,我們來建立子問題的最優值的遞迴關係。用c[i,j]記錄序列Xi和Yj的最長公共子序列的長度。其中Xi=<x1, x2, …, xi>,Yj=<y1, y2, …, yj>。當i=0或j=0時,空序列是Xi和Yj的最長公共子序列,故c[i,j]=0。其他情況下,由定理可建立遞迴關係如下:
程式碼如下:
- 1. #include <cstdio>
- 2. #include <iostream>
- 3. #include <algorithm>
- 4. #include <cstring>
- 5. usingnamespace std;
- 6.
- 7. int main()
- 8. {
- 9. string str1,str2;
- 10. int dp[200][200];
- 11. while(cin>>str1>>str2)
- 12. {
- 13. memset(dp,0,sizeof(dp));
- 14.
- 15. int la = str1.length(); //字串的長度
- 16. int lb = str2.length();
- 17.
- 18. for(int i = 1; i <= la; i++)
- 19. for(int j = 1; j <= lb; j++)
-
相關推薦
演算法(一) --DP動態規劃(LIS和LCS)
1.http://blog.csdn.net/u013445530/article/details/45645307 DP問題是ACM裡面最難的,因為太考思維能力了,只有將狀態轉移方程推出來才能解決問題,DP問題也是面試的時候最容易考到的,希望大家好好學DP,至少在面試的時
【演算法之動態規劃(一)】動態規劃(DP)詳解
一、基本概念 動態規劃(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。20世紀50年代初美國數學家R.E.Bellman等人在研究多階段決策過程(multistep d
(轉)dp動態規劃分類詳解
fun balance card 給定 def bits eve 回文串 好的 dp動態規劃分類詳解 轉自:http://blog.csdn.NET/cc_again/article/details/25866971 動態規劃一直是ACM競賽中的重點,同時又是難點,因為
九章演算法高階班筆記6.動態規劃(下)
區間類DP Stone Game Burst Ballons Scramble String 匹配類動規 Longest Common Subsequence Edit Distance K Edit Distance Distinct Subqu
九章演算法高階班筆記5.動態規劃(上)
大綱: cs3k.com 滾動陣列 House Robber I/II Maximal Square 記憶化搜尋 Longest Increasing Subsequence Coin in a line I/II/III 什麼是動態
單調佇列優化DP動態規劃(1)
一.前言 學好了單調佇列不僅可以單獨使用,他還可以有更多的廣泛用途。這裡我主要講其對動態規劃的優化,較簡單。 二.例題1:單調佇列本身的靈活應用——測量溫度 在講用單調佇列優化DP前要先講一講單調佇列本身的靈活應用,所以引入一道題目——測量溫度。 1.題目 題目描述
演算法課堂實驗報告(四)——python動態規劃(最長公共子序列LCS問題)
python實現動態規劃 一、開發環境 開發工具:jupyter notebook 並使用vscode,cmd命令列工具協助程式設計測試演算法,並使用codeblocks輔助編寫C++程式 程式語言:python3.6 二、實驗內容 1.最長公共子序列問題。分別求x=
caioj1063·動態規劃入門(一維一邊推1:美元和馬克)
1063:動態規劃入門(一維一邊推1:美元和馬克) 時間限制: 1 Sec 記憶體限制: 128 MB 題目描述 【問題描述】 今天6:00起床,我轉身發現枕頭邊有100美元。 出門的時候發現門口有家冰淇淋店,拉了很長的橫幅:“今天100美元和4
Just Too Lucky(一個數能被其各位的和整除)
題目連結https://vjudge.net/problem/Gym-100623J 題目大意:求1-n中,有多少個數,可以被其各位的和整除 n<=1e12 設x為數位和 x的範圍為[1,108] 也就是說要讓一個數n mod x = 0 &nbs
一文弄懂動態規劃(DP Dynamic Programming)下樓梯,國王和金礦,揹包問題,Dijkstra演算法
動態規劃 參考連結 漫畫演算法,什麼是動態規劃? DP 動態規劃是一種分階段求解決策問題的數學思想 題目一 問:下樓梯問題,有一座高度是10級臺階的樓梯,從下往上走,每跨一步只能向上1級或者2級臺階,請問有多少中走法。 思路 剛才這個題目,你每走一步就有兩
演算法設計與分析——動態規劃(一)矩陣連乘
動態規劃——Dynamic programming,可以說是本人一直沒有啃下的骨頭,這次我就得好好來學學Dynamic programming. OK,出發! 動態規劃通常是分治演算法的一種特殊情況,它一般用於最優化問題,如果這些問題能夠: 1.能夠分解為規模更小的子問題 2.遞迴的
動態規劃(DP)演算法
動態規劃相信大家都知道,動態規劃演算法也是新手在剛接觸演算法設計時很苦惱的問題,有時候覺得難以理解,但是真正理解之後,就會覺得動態規劃其實並沒有想象中那麼難。網上也有很多關於講解動態規劃的文章,大多都是敘述概念,講解原理,讓人覺得晦澀難懂,即使一時
由Leetcode詳解演算法 之 動態規劃(DP)
因為最近一段時間接觸了一些Leetcode上的題目,發現許多題目的解題思路相似,從中其實可以瞭解某類演算法的一些應用場景。 這個隨筆系列就是我嘗試的分析總結,希望也能給大家一些啟發。 動態規劃的基本概念 一言以蔽之,動態規劃就是將大問題分成小問題,以迭代的方式求解。 可以使用動態規劃求解的問題
演算法總結之動態規劃(DP)
適用動態規劃的特點 所解決的問題是最優化問題。 所解決的問題具有“最優子結構”。可以建立一個遞推關係,使得n階段的問題,可以通過幾個k<n階段的低階子問題的最優解來求解。 具有“重疊子結構”的特點。即,求解低階子問題時存在重複計算。 詞典法 大家都知道,遞迴演算法一般都存在大量的重複計算,這會造成不
Dijkstra演算法,求最短路(dp 動態規劃)
•迪傑斯特拉(Dijkstra)演算法思想 按路徑長度遞增次序產生最短路徑演算法: 把V分成兩組: (1)S:已求出最短路徑的頂點的集合 (2)V-S=T:尚未確定最短路徑的頂點集合 將T中頂點按最短路徑遞增的次序加入到S中, 保證:(1)從源點V0到S中各
演算法筆記:動態規劃(DP)初步
專題:動態規劃(DP)初步 內容來源:《挑戰程式設計競賽》(第2版)+《演算法競賽入門經典》(第2版)+網上資料整理彙總 一、引入 動態規劃程式設計是對解最優化問題的一種途徑、一種方法,而不是一種特殊演算法。不像前面所述的那些搜尋或數值計算那樣,具有
演算法之動態規劃(DP)
前言 前言_ 我們遇到的問題中,有很大一部分可以用動態規劃(簡稱DP)來解。 解決這類問題可以很大地提升你的能力與技巧,我會試著幫助你理解如何使用DP來解題。 這篇文章是基於例項展開來講的,因為乾巴巴的理論實在不好理解。 注意:如果你對於其中某一節已經瞭解並且不
五個常用演算法(一):動態規劃
1.從01揹包問題說起 有一堆寶石一共n個,現在你身上能裝寶石的就只有一個揹包,揹包的容量為C。把n個寶石排成一排並編上號: 0,1,2,…,n-1。第i個寶石對應的體積和價值分別為V[i]和W[i] 。揹包總共也就只能裝下體積為C的東西,那你要裝下哪些寶石才能獲得最大的
動態規劃(DP)
first 個數 目的 進入 right 返回值 ase lines cal 在學習動態規劃前 , 先補充些有關遞歸的知識 。 所謂的遞歸函數 就是調用自身函數的過程 ,因此是用棧來存儲的 。 遞歸函數的最終返回值 就是第一次調用函數的返回值 。 在寫
初探動態規劃(DP)
isp exit min 管理 應該 一行 註意 習慣 試圖 學習qzz的命名,來寫一篇關於動態規劃(dp)的入門博客。 動態規劃應該算是一個入門oier的坑,動態規劃的抽象即神奇之處,讓很多萌新 萌比。 寫這篇博客的目標,就是想要用一些容易理解的方式,講解入門動態規劃的真