LQ训练营(C++)学习笔记_常见动态规划模型
常見動態規劃模型
- 六、常見動態規劃模型
- 1、最大字段和
- 1.1 概念描述
- 1.2動態規劃算法分析
- 1.3 代碼實現
- 2、最長上升子序列(LIS)
- 2.1 概念描述
- 2.2 算法分析
- 2.3 代碼實現
- 3、最長公共子序列
- 3.1 概念描述
- 3.2 算法分析
- 3.3 代碼實現
六、常見動態規劃模型
1、最大字段和
1.1 概念描述
給定一個由數字組成的序列,其中連續的一段子序列稱為一個子段,子段中的所有數之和稱為子段和,這里只考慮非空子段,即至少包含一個元素的子段,一個序列中子段和最大的子段的子段和,為最大子段和。
1.2動態規劃算法分析
對于全部是非正數的序列,最大子段和為其中元素的最大值。
對于有正數的序列,考慮以每一個點為結尾的最大子段和,這個子段一定滿足其前綴和均非負,因為如果有一個前綴是負的,那么減掉這個前綴對于這個點一定更優,并且這個子段要盡量往前延伸。
我們可以只用一次掃描,記錄目前統計的sum以及答案ans。當sum加上當前位置這個數還是正數的時候就繼續累加sum,否則就將sum置為0 。這樣就舍掉了所有前綴是負數的情況,并且保證了這個子段盡可能的長,也就是說掃描中記錄的sum就是以每一個點為結尾的最大子段和,每一次如果sum比ans大就可以更新ans 。最后ans就是整體的最大子段和。
1.3 代碼實現
#include<iostream> #include<algorithm> using namespace std; const int inf = 0x7fffffff; int num[101]; int main(){int N;cin>>N;//讀入N和N個變量for(int i=0;i<N;i++){cin>>num[i];}int ans=-inf;//聲明ans并初始化for(int i=0;i<N;i++){//遍歷數組有沒有正數,并找出最大值ans=max(ans,num[i])}if(ans<=0){//若ans非負,輸出cout<<ans<<endl;}else{int sum=0;//聲明sum,并初始化為0for(int i=0;i<N;i++){if(sum+num[i]<0){//如果加上后為負數,就舍棄這一段重新開始sum=0;}else{//否則將sum加上num[i]sum+=0;}ans=max(ans,sum);//看看是否需要更新}}cout<<ans<<endl;return 0; }2、最長上升子序列(LIS)
2.1 概念描述
在原序列取任意多項,在不改變他們原來數列的先后順序,得到的序列稱為原序列的子序列。最長上升子序列,就是給定的序列的最長的,數值從低到高排列的的子序列。
2.2 算法分析
所以我們需要描述一下這道題,很顯然這題用一維就可以去描述了,而這題的階段就是以第i項作為結尾端點的上升子序列(即dp[i]),而這題的狀態就是第i個位置的最長上升子序列的長度,決策的話就是第i個位置是否加入要計算的最長上升子序列(a[j]<a[i],j<i這樣的情況才能加入),所以我們就可以推出來他的轉移方程:,其實這就是一個帶有最優子結構的遞推轉移方程,如果滿足加入最長上升子序列的條件后我們并不一定非得必須加入(因為之前還有可能有以他為結尾的更長的上升子序列)。需要注意的一點是我們需要用兩層for循環來找出他的最長上升子序列長度(因為最長上升子序列不是唯一的,所以每一個起點都要去試一下)。
這是遞推打表之后得到的模擬表:
2.3 代碼實現
#include<iostream> #include<cstring> using namespace std; int dp[101],a[101],n; int LIS(){int ans=0;for(int i=1;i<=n;i++){dp[i]=1;for(int j=1;j<i;j++){if(a[j]<a[i]){dp[i]=max(dp[i],dp[j]+1);//狀態轉移方程}}ans=max(ans,dp[i]);//每次內層循環結束后更新ans的值}return ans; } int main(){cin>>n;for(int i;i<=n;i++){cin>>a[i];}cout<<LIS()<<endl;return 0; }3、最長公共子序列
3.1 概念描述
給定兩個序列S1和S2,求兩者公共子序列S3的最長長度。
3.2 算法分析
這個題目可以按照序列的長度來劃分狀態,也就是S1的前i個字符和S2的前j個字符的最長公共子序列長度,記為lcs[i][j].
如果S1的第i項和S2 的第j項相同,那么S1[i]與S2[j]作為公共子序列的末尾,則
lcs[i][j]=lcs[i-1][j-1]+1
也可以不讓S1[i]與S2[j]作為公共子序列的末尾,則
lcs[i][j]=max(lcs[i][j-1],lcs[i-1][j])
不難證明:
max(lcs[i][j-1],Ics[i-1][j])< Ics[i-1][j-1]+1
那么轉移方程是:
舉一個例子,兩個序列 S1? = abcfbc和S2? = abfcab,根據轉移方程可以得出 lcs[i][j]
3.3 代碼實現
#include <iostream> #include <cstring> #include <string> using namespace std; int dp[110][110];//定義一個輔助數組 int main() {string a, b;memset(dp, 0, sizeof(dp));//將dp數組初始化cin >> a >> b;//輸入兩個字符串a、bint lena = a.size();//計算字符串a、b的長度int lenb = b.size();for (int i = 1; i <= lena; i++){for (int j = 1; j <= lenb; j++){if (a[i - 1] == b[j - 1]){dp[i][j] = dp[i - 1][j - 1] + 1;//dp[i][j]代表a字符串的前i個字符串組成的子串和b字符串的前j個字符串組成的子串LCS}else{dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}}cout << dp[lena][lenb] << endl;return 0; }總結
以上是生活随笔為你收集整理的LQ训练营(C++)学习笔记_常见动态规划模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win10怎么关闭通知 win10关闭通
- 下一篇: LQ训练营(C++)学习笔记_背包问题