『经典DP入门』LIS最长上升子序列的三种不同DP方法

最长上升(也有译作递增)子序列问题简称LIS(Longest Increasing Subsequence)问题,其数学模型如下:

设A =  <a1,a2,...,an>是n个不同的实数的序列,A的上升子序列是这样的一个子序列L =< a_{k_{}1},a_{k_{}2},...,a_{k_{}n}>,

其中k_{1} < k_{2} <...<k_{m}a_{k_{}1}<a_{k_{}2}<...<a_{k_{}n}。求最大的m值。

一共有三种DP方法 可用于最长上升子序列问题

  1. 将LIS问题转化为LCS问题;
  2. 经典DP方法;
  3. 二分查找。

『方法一』转化为LCS问题


设序列X = <b_{1},b_{2},...,b_{n}>是对序列A =  <a1,a2,...,an>按上升序列排好序的序列。显然X和A的最长公共子序列就是A的最长上升序列。这个原理比较简单就直接给出代码。

#include 
using namespace std;
int dp[10][10];
int A[] = {1, 7, 3, 5, 9, 4, 8},X[7];
void dp_lcs() {
	for(int i = 0 ;  i < 7; i++) {
		dp[i][0] = 0;
		dp[0][i] = 0;
	}
	for(int i = 1 ;  i <= 7; i++) {
		for(int j = 1 ; j <= 7; j++) {
			if(A[i-1] == X[j-1])
				dp[i][j] = dp[i-1][j-1]+1;
			else
				dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
		}
	}
}
int main() {
	for(int i = 0 ; i < 7; i++) {
		X[i] = A[i];
	}
	sort(A,A+7);
	//开始LCS
	dp_lcs();
	for(int i = 1 ; i <= 7; i++) {
		for(int j = 1; j <= 7; j++) {
			cout<dp[i] = \begin{cases} 1& \text{ if } j= 1\\ max(dp[j]\,\,|\,\,a_{j}<a_{i})(1 \leq j\leq i-1)+1& \text{ if } i\neq 1 \end{cases}

『代码』

#include 
#include 
#include 
#include 
using namespace std;
int dp[100010];
int a[100010];
int ans = 1;
int main(){
	int n,maxx = 0,k;
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
		scanf("%d",a+i);
/*************核心部分*****************/ 
	for(int i = 1; i <= n; i++){
			dp[i] = 1;
		for(int j = 1; j < i; j++){
			if(a[i] > a[j])
			dp[i] = max(dp[i],dp[j]+1);
		}
		ans = max(ans,dp[i]);
	}
/*************************************/
	cout<

『方法3』二分查找


第二种DP方法在计算么一个dp[i]时,都要找出最大的dp[j](1 <= j

这里使用一个辅助数组b来存储子序列的LIS,b[len]存的就是LIS的尾元素(即最大的元素)。

1.当b[len] < a[i]:说明a[i]能接在d[len]后面,长度增加,直接 b[++len] = a[i];

2.否则,找到 b(LIS)中第一个大于等于a[i]的元素 b[k],令 b[k] = a[i],b[1] 的初始值为 a[1],len初始值为1。

3.最终的len就是我们要求的答案。

举个栗子:

5

19 11 10 7 8

初始化 len = 1,b[1] = a[1] = 19,LIS = {19};

 i = 2时,a[2] < b[len(1)],找到第一个大于等于a[2]的元素b[1] = 19,使b[1] = 11;

 i = 3时,a[3] < b[len(1)],找到第一个大于等于a[3]的元素b[1] = 11,使b[1] = 10;

 i = 4时,a[4] < b[len(1)],找到第一个大于等于a[3]的元素b[1] = 10,使b[1] = 7;

 i = 5时,a[5] < b[len(1)],len++,使b[len(2)] = a[5]。

 len = 2,算法结束。

『代码』

#include 
#include 
#include 
#include 
using namespace std;
int dp[100010];
int a[100010],b[100010];//b用来保存LIS
int ans = 1;
int main() {
	freopen("data.in","r",stdin);
	int n,maxx = 0,k;
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
		scanf("%d",a+i);
	/*核心部分******************************/
	int len = 1;
	b[1] = a[1];
	for(int i = 2; i <= n; i++) {
		if(b[len] <= a[i])
			b[++len] = a[i];
		else
			*lower_bound(b+1,b+len,a[i]) = a[i];//此处使用了STL优化 lower_bound本身就是用二分法封装的
	}
	/*************************************/
	cout<<"max:"<


 

你可能感兴趣的:(学不会的DP)