2017級演算法模擬上機準備篇(序列DP 初階)
序列DP 是一類蠻有意思的DP 序列區別於陣列的地方就在於沒有要求連續性
從一道簡單的序列DP開始:
SkyLee炒股票(最大連續子序列和)
這道題其實用DP有點浪費的感覺,其實這道題用簡單的遍歷法也可以,不過實質上還是DP思想的進階。
這道題如何設定DP陣列?我們不妨設定DP[i]為以陣列元素ar[i]結尾的連續序列的最大和.
所以我們很容易得出狀態轉移方程:
dp[i]=max(dp[i-1] + ar[i] , ar[i]);
倘若之前的dp[i-1]再加上當前元素 得到的值更大,那麼最大連續子序列的長度就會延長,如果沒有那麼就可以使其單個元素成為一個連續子序列。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int Maxlen = 1e6 + 10;
long long num[Maxlen];
long long dp[Maxlen];//dp[i]代表的是以i元素結尾的連續子序列的最大值
int main(int argc, char *argv[]) { int n,i,j,k; long long ans; while(~scanf("%d",&n)){ for(i=1;i<=n;i++) scanf("%lld",&num[i]); dp[0]=0; ans=0; for(i=1;i<=n;i++){ dp[i]=max(dp[i-1]+num[i],num[i]); ans=max(ans,dp[i]); } printf("%lld\n",ans); } return 0; }
那如果我們要繼續求解最大連續子序列和的起點和終點的位置呢?
顯然只需要利用dp陣列進行一次搜尋即可。
if(dp[i]>ans){
ans=dp[i];
right=i;
left=i;
while(dp[left] > 0 && left >=1)
left--; if(left ==0 || dp[left] < 0) left++; }
接下來再來求解一道經典的序列DP 問題 LIS
ModricWang的導彈攔截系統(最長不上升子序列 LIS)
這道題其實就是求解最長不上升子序列長度。
我們首先思考DP陣列的設定:dp[i]代表的是以陣列元素ar[i]為結尾元素的不上升子序列長度。
那轉移方程呢?
for(i=1;i<=n;i++){
for(j=1;j<i;j++){
if(ar[j]>=ar[i])
dp[i]=max(dp[j]+1,dp[i]);
}
ans=max(ans,dp[i]);
}
其實到這一步 就可以簡單的總結一下序列DP的解法。
做DP題的第一步就是合理的構造有確定含義 符合最優子結構 的DP陣列
然後是實現狀態轉移方程,其第一步的構思和第二部略有重複。最後實現一下DP陣列的初始化,就可以完成了。
序列DP的dp陣列的設定通常具有的含義是 以給定陣列ar[i]結尾的具有某種特殊含義的陣列,然後繼續思考狀態轉移方程即可。
由於序列只有前後的邏輯關係,所以子問題和父問題,可以簡單的理解為前後關係。
同時序列DP的最值問題,通常在遍歷求解DP陣列的同時,更新即可。
最長公共子序列(LCS)
給定兩個序列,求解最長公共子序列的問題。
根據上述設定dp陣列的思路 我們不妨設定 dp[i][j] 其中i為序列1 以下標i結尾 j為序列2 以下標j結尾
狀態轉移方程:
if(s1[i]==s2[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
最長上升公共子序列(LCIS)
http://acm.hdu.edu.cn/showproblem.php?pid=1423(原題連結)
顯然這道題是基與LCS的,那麼如何求解上升的子問題,我們還是按照序列dp一貫的思路設定dp陣列:
設定 dp[i][j] 其中i為序列1 以下標i結尾 j為序列2 以下標j結尾 顯然這些是不夠的
我們在此之上可以 新增一點條件:我們不妨設定dp[i][j]的公共子序列的末尾元素是dp[j]即:
dp[i][j]表示以a串的前i個整數與b串的前j個整數且以b[j]為結尾構成的LCIS的長度
那麼我們來實現狀態轉移方程:
if(a[i] != b[j])
dp[i][j]=dp[i-1][j];
else {
for(k=1;k<=j-1;k++)
if(b[j]>b[k])
dp[i][j]=max(dp[i][j],dp[i-1][k]+1); }
從這道題我們可以總結出來序列DP的一些其他思想,如果存在兩個以上變化的量,我們可以考慮以一個變數為標準,相當於確定了一個變數,這樣思考另一個變數帶來的影響更簡潔。
中等·Bamboo's Fight with DDLs II (類似LCS的序列DP問題)
dp[i][j]代表的是序列區間是從i到j的最長迴文子序列長度
if(str[i] == str[j])
dp[i][j]=2+dp[i+1][j-1];
else dp[i][j]=max(dp[i+1][j],dp[i][j-1]);