动态规划之,最长公共子序列,最长上升子序列,最长公共递增子序列的长度

创作来自 

[ACM_动态规划] UVA 12511 Virus [最长公共递增子序列 LCIS 动态规划]

首先是LCS,即最长公共子序列问题。
给定两个字符串s1s2...sn和t1t2...tn。求出两个字符串最长的公共子序列的长度。(字符串可以是数字)
定义dp数组。
dp[i][j]表示s1s2...si和t1t2...tj对应的LCS长度

由此,s1...s(i+1)和t1...t(j+1)对应的公共子序列可能是:

当s(i+1) = t(j+1)时,在s1s2...si和t1t2...tj的公共子序列末尾追加上s(i+1)

s1s2...si和t1t2...t(j+1)的公共子序列

s1s2...s(i+1)和t1t2...tj的公共子序列

三者中的某一个。

递推关系:
dp[i+1][j+1] = 
max(dp[i][j]+1,dp[i][j+1],dp[i+1][j])(Si+1 = Tj+1)
max(dp[i][j+1],dp[i+1][j])

下面是模板,请勿生吃:!(来自《挑战程序设计竞赛》)
//输入
int n,m;
char s[maxn],t[maxn];

int dp[maxn+1][maxn+1];//DP数组

void solve(){
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(s[i]==t[i]){
				dp[i+1][j+1] = dp[i][j]+1;
			}else{
				dp[i+1][j+1] = max(dp[i][j+1],dp[i+1][j]);
			}
		}
	}
	
	cout<<dp[n][m]<<endl;
} 


其次是LIS,即最长上升子序列问题。

长为n的数列,a0,a1,...,an-1。求出这个序列中最长的上升子序列的长度。(满足任意i<j,ai<aj的序列)

定义dp数组:

dp[i]是以ai为末尾的最长上升子序列的长度


递推关系:

dp[i] = max{1,dp[j]+1|j<i且aj<ai}

可以在O(n2)时间内解决问题。


模板如下:

//输入
int n;
int a[maxn];

int dp[maxn];//DP数组

void solve(){
	int res = 0;
	for(int i=0;i<n;i++){
		dp[i] = 1;
		for(int j=0;j<i;j++){
			if(a[j]<a[i]){
				dp[i] = max(dp[i],dp[j]+1);
			}
		}
		res = max(res,dp[i]);
	}
	
	cout<<res<<endl;
} 

最后是。。LCIS。。

其实最终我还是不能够太理解这个做法,看还是看懂了,,但是感觉自己肯定写不出来,希望大神能够指点指点,可以让我完全领悟大哭

定义dp数组。。

dp[i][j]表示以a串前i个字符b串的前j个字符且以b[j]为结尾构成的LCIS的长度
     当a[i+1]!=b[j+1]时:dp[i+1][j]=dp[i][j]
     当a[i+1]==b[j+1]时:dp[i+1][j]=max(dp[i][k])+1 1<=k<=j-1 && b[j]>b[k]
(代码中的i和解释中的i差了一位~~)
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1005;
int a[maxn];
int b[maxn];
int dp[maxn][maxn];
int main() {
	int t;
	cin>>t;

	while(t--) {
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		int x,y;
		cin>>x;

		for(int i=1; i<=x; i++)
			cin>>a[i];

		cin>>y;
		for(int i=1; i<=y; i++)
			cin>>b[i];

		int res = 0;
		memset(dp,0,sizeof(dp));

		for(int i=1; i<=x; i++) {
			int sum = 0;
			for(int j=1; j<=y; j++) {
				dp[i][j] = dp[i-1][j];
				if(a[i]>b[j]&&sum<dp[i-1][j]) sum = dp[i-1][j];
				if(a[i] == b[j]) dp[i][j] = sum + 1;
			}
		}

		for(int i=1; i<=y; i++) {
			if(res<dp[x][i]) {
				res = dp[x][i];
			}
		}
		cout<<res<<endl;
	}
}
这是湖南第八届省赛的一道题,可以直接过的
https://vjudge.net/contest/161005#problem/J
这道题可以优化dp数组,变成一维因为每次i都是从上一行的结论得到的。。。

望得到大神的指点。。。。
展开阅读全文

没有更多推荐了,返回首页