583 字
3 分钟
【OI考古】动态规划 | 动态规划基础

动态规划(Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。

最长上升子序列(LIS)#

给出一个序列 a1,a2,...,an1,ana_1, a_2, ...,a_{n-1}, a_n ,求其最长不下降(或上升)子序列。

求最长上升子序列(LIS)的算法是Yukko介绍给我的第一个算法。

解决方案( O(n2)O(n^2)#

状态设计:#

dp[i]dp[i] 表示以第 ii 个数结尾的最长上升子序列的最长长度。

状态转移方程:#

dp[i]=max(dp[j])+1(1ji,且a[i]>a[j])dp[i]= max(dp[j])+1 \quad (1≤j<i, 且a[i]>a[j])

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 100500;
int n, ans;
int a[maxn], dp[maxn];
int main()
{
	scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
        dp[i] = 1;	
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < i; j++)
        {
            if (a[i] > a[j] && dp[i] < dp[j] + 1)	//如果a[i] > a[j],则a[i]可以接在以a[j]结尾的上升子序列后面
                dp[i] = dp[j] + 1;	//如果dp[i] < dp[j] + 1,则可以用dp[j] + 1来更新dp[i]
        }
        ans = max(ans, dp[i]);	//取dp[i]的最大值来作为ans
    }
    printf("%d", ans);
    return 0;
}

解决方案( O(nlogn)O(n\log_{}{n})#

例题#

洛谷 P1020 | [NOIP1999 普及组] 导弹拦截

洛谷 P1091 | [NOIP2004 提高组] 合唱队形

最长公共子序列(LCS)#

模板题:洛谷 P1439 | [模板]最长公共子序列#

题目描述#

给出 1,2,,n1,2,\ldots,n 的两个排列 P1P_1P2P_2 ,求它们的最长公共子序列。

输入格式#

第一行是一个数 nn

接下来两行,每行为 nn 个数,为自然数 1,2,,n1,2,\ldots,n 的一个排列。

输出格式#

一个数,即最长公共子序列的长度。

输入输出样例#

输入 #1#
5 
3 2 1 4 5
1 2 3 4 5
输出 #1#
3
数据规模与约定#
  • 对于 50%50\% 的数据, n103n \le 10^3
  • 对于 100%100\% 的数据, n105n \le 10^5

解决方案#

状态设计:#

dp[i][j]dp[i][j] 表示 P1P_1ii 位, P2P_2jj 位的最长公共子序列长度。

状态转移方程:#

dp[i][j]={max(dp[i][j],dp[i1][j1]+1)if P1[i]=P2[j]max(dp[i1][j],dp[i][j1])if P1[i]P2[j]dp[i][j]=\begin{cases}max(dp[i][j], dp[i-1][j-1] + 1) &\text{if } P_1[i]=P_2[j] \\ max(dp[i-1][j], dp[i][j-1]) &\text{if } P_1[i]\not =P_2[j] \end{cases}

请参阅#

【OI考古】动态规划 | 动态规划基础
https://blog.vonbrank.com/posts/oi-dp-basic-dp-algorithm/
作者
Von Brank
发布于
2021-03-24
许可协议
CC BY-NC-SA 4.0