DP算法入门(2)

文章目录

  • 线性规划
    • 例题
    • 学习秘籍
  • 最长上升子序列
    • 例题
    • 求解过程

线性规划

例题

先看一道 I O I IOI IOI 原题 :

IOI1994数字三角形 Number Triangles–洛谷

DP算法入门(2)_第1张图片

具体题目描述见题目

状态表达: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从顶部走到点 ( i , j ) (i,j) (i,j) 经过的数字最大值。

状态转移方程:

d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) dp[i][j]=max(dp[i-1][j-1],dp[i-1][j]) dp[i][j]=max(dp[i1][j1],dp[i1][j])

初值(边界条件) : d p [ 1 ] [ 1 ] = a [ 1 ] [ 1 ] dp[1][1]=a[1][1] dp[1][1]=a[1][1]

目标 : m a x ( d p [ n ] [ j ] ) max(dp[n][j]) max(dp[n][j]) j ∈ [ 1 , n ] j∈[1,n] j[1,n]

D P DP DP 代码:

#include
using namespace std;
int dp[1024][1024];
int a[1024][1024],m;
int solve_1(int n){
	int ans=0;
	dp[1][1]=a[1][1];
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++)
			dp[i][j]=a[i][j]+max(dp[i-1][j],dp[i-1][j-1]);
	for(int i=1;i<=n;i++)ans=max(ans,dp[n][i]);
	return ans;
}
int dp_1[10005];
int solve_2(int n){
	int ans=0;
	dp_1[1]=a[1][1];
	for(int i=2;i<=n;i++){
		for(int j=i;j>=1;j--){
			dp_1[j]=max(dp_1[j],dp_1[j-1])+a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		ans=max(ans,dp_1[i]);
	}
	return ans;
}
int main(){
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		for(int j=1;j<=i;j++){
			scanf("%d",&a[i][j]);
		}
	}
	printf("%d",solve_2(m));
	return 0;
}

学习秘籍

( 1 ) (1) (1) 为什么这么做?

多想一下

( 2 ) (2) (2) 想不出状态转移方程怎么办?

多问一句

( 3 ) (3) (3) 举一反三,拓展思维

需要有尝试的勇气,打开思维。

最长上升子序列

例题

题目–洛谷

子序列 : 只能从前往后取。

如对这样一个序列 ( 1 , 3 , 5 , 9 , 7 , 8 , 4 ) (1,3,5,9,7,8,4) (1,3,5,9,7,8,4)

它的子序列就有

( 1 , 5 , 9 , 8 , 4 ) (1,5,9,8,4) (1,5,9,8,4) 当然,这不是上升子序列。

( 1 , 3 , 5 , 7 , 8 ) (1,3,5,7,8) (1,3,5,7,8) 这样的子序列才叫上升子序列

简而言之,就是子序列中的元素必须满足单调递增

现在给你一个数列,请你输出他的最长的子序列的长度。

输入 :

7
1 7 3 5 9 4 8

输出 :

4

求解过程

( 1 ) (1) (1) 确定状态: d p [ i ] dp[i] dp[i] 表示以 a [ i ] a[i] a[i] 结尾的最长上升子序列长度。

( 2 ) (2) (2) 状态转移: 对于 1 ≤ j < i 1\le j1j<i , 若 a [ j ] < a [ i ] a[j] < a[i] a[j]<a[i] , 则可以将 a [ i ] a[i] a[i] 放在以 a [ i ] a[i] a[i] 结尾的最长上升序列后面,得到的长度为 d p [ j ] + 1 dp[j]+1 dp[j]+1

( 3 ) (3) (3) 边界条件: d p [ 0 ] = 0 dp[0]=0 dp[0]=0

( 4 ) (4) (4) 求解目标: m a x ( d p [ i ] ) max(dp[i]) max(dp[i])

状态转移方程:

d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) , 1 ≤ j < i dp[i]=max(dp[i],dp[j]+1), 1 \le j dp[i]=max(dp[i],dp[j]+1),1j<i

时间复杂度 O ( n 2 ) O(n^2) O(n2)

代码实现:

#include
#include
using namespace std;
int len,dp[100005],num[100005],ans;
int main(){
	scanf("%d",&len);
	for(int i=1;i<=len;i++)scanf("%d",&num[i]);
	for(int i=1;i<=len;i++){
		dp[i]=1;
		for(int j=1;j<i;j++){
			if(num[i]>num[j])dp[i]=max(dp[i],dp[j]+1);
		}
	}
	for(int i=1;i<=len;i++)ans=max(ans,dp[i]);
	printf("%d",ans);
	return 0;
}

设置一个辅助数组 d [ l e n ] d[len] d[len]

d d d 数组 表示最长上升子序列, d [ i ] d[i] d[i] 表示最长上升子序列的第 i i i 项。(其实也不一定是,不信你试试)

输入

5
1 8 9 10 2

不过输出到是正确的……

我们要符合这样一个规则:

1. 1. 1. n u m [ i ] num[i] num[i] d [ l e n ] d[len] d[len] 进行比较

( 1 ) (1) (1) n u m [ i ] > d [ l e n ] num[i]>d[len] num[i]>d[len] 则将 n u m [ i ] num[i] num[i] 放到 d [ l e n ] d[len] d[len] 的后面。

( 2 ) (2) (2) n u m [ i ] < d [ l e n ] num[i]num[i]<d[len] 则将 n u m [ i ] num[i] num[i] 替换为 第一个大于等于 d [ l e n ] d[len] d[len]

时间复杂度 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

代码:

#include
using namespace std;
int d[10005],a[10005];
int k,n,maxx,z,len=1;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	d[1]=a[1];
	for(int i=2;i<=n;i++){
		if(a[i]==d[len])continue;
		if(a[i]>d[len])d[++len]=a[i];
		else{
			//*表示内容 
			*lower_bound(d+1,d+len+1,a[i])=a[i];
		}
	}
	printf("%d",len);
	return 0;
}

大家点一下关注吧!也可以点一个免费的赞,谢谢啦!

你可能感兴趣的:(DP算法,算法,动态规划)