动态规划入门-LIS/LCS(2020-05-21)

写在前面:学DP掌握基础很重要,这里记录一下LIS和LCS,(希望每次在记录时能够收获新的东西

引入问题:

给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。

N有这两个数据范围:
1.
2.

样例
Input:
7
3 1 2 1 8 5 6
output:
4

一.1≤N≤1000

对序列中一个的数a[i]来说,我们定义dp[ i ] 表示以a[i]这个数结尾的最长上升子序列的长度,那么对于下标j∈(1~i-1)这些数来说,如果a[ i ]严格大于a[ j ],就有dp[i] = dp[j]+1,时间复杂度为O(n^2),在这个数据范围内是可以ac的。

#include 
#include 
#include 

using namespace std;

const int N = 1010;

int n;
int dp[N],w[N];

int main()
{
    cin>>n;
    for(int i = 1; i<=n; i++) cin>>w[i];
    
    for(int i = 1; i<=n; i++)
    {
        dp[i] = 1;
        for(int j = 1; j<=i-1; j++)
            if(w[i]>w[j])
                dp[i] = max(dp[i], dp[j]+1);
    }
    
    int ans = 0;
    for(int i = 1; i<=n; i++) ans = max(ans,dp[i]);
    
    cout<

二.1≤N≤100000

对于这个数据范围来说,上面那种做法显然回超时。那么该如何解决这个问题?
假设我们当前已经走到了第i个数,那么我们肯定想在前i-1个数中拿到最长的那个上升子序列并且可以让a[i]接在后面的,那么我们可以使用一个数组来存储这样一个信息:在前i-1数字中长度为len的子序列的最后一个数字的最小值,为什么是最小值,因为最小值的适用范围更广(如果能接在大的数后面,那么肯定也能接在这个最小值后面),而且,易证,随着len的增加,序列最后的最小值也是严格递增的(反证法可证),那么我们就可以使用二分,找到子序列长度0~len中,最后一个数小于a[ i ]中最大的那一个,然后更新答案。

#include 
#define IO ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
typedef long long LL;

const int N = 1e5+100;

int n;
int a[N];
int q[N];

int main()
{
    IO;
    cin>>n;
    for(int i = 1; i<=n; i++) cin>>a[i];
    
    q[0] = -2e9;  //哨兵,当前数比前面的数都小
    int len = 0;
    for(int i = 1; i<=n; i++){
        int l = 0, r = len;
        while(l>1;
            if(q[mid]

洛谷P1439.最长公共子序列

给出 1,2,...,n 的两个排列 P1和P2 ,求它们的最长公共子序列。
输入
5
3 2 1 4 5
1 2 3 4 5
输出
3
Hint:对于 100% 的数据,n≤10^5

思路:因为是1到n的全排列,所以P1和P2只是元素顺序不同,可以这样转换:
P1: 3 2 1 4 5
change:a b c d e
P2:1 2 3 4 5
=> c b a d e
经过这样一个转换,P1中的元素全是严格单调递增的,那么对于P2,只要将它的元素都改成change中的对应元素,然后求最长上升子序列,那么就一定是两个序列最长的公共子序列

#include 
#define IO ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
typedef long long LL;

const int N = 1e5+100;
int n;
int b[N],q[N];
unordered_map h;

int main()
{
    IO;
    cin>>n;
    for(int i = 1; i<=n; i++){
        int num;
        cin>>num;
        h[num] = i;
    }
    
    for(int i = 1; i<=n; i++){
        cin>>b[i];
        b[i] = h[b[i]];
    }
    
    q[0] = -2e9;
    int len = 0;
    for(int i = 1; i<=n; i++){
        int l = 0, r = len;
        while(l>1;
            if(q[mid]

你可能感兴趣的:(动态规划入门-LIS/LCS(2020-05-21))