动态规划-LCS、LIS

L C S LCS

LCS(Longest Common Subsequence)最长公共子序列。给定两个序列a和b,当另一序列c即是a的子序列又是b的子序列时,称c时a和b的公共子序列,最长公共子序列时所有子序列中长度最长的。
注意子序列是在原序列中删去若干元素后得到的序列,注意子序列≠子串,子串在原序列中是连续的。
比如序列a={1,3,2,5,4}和b={1,4,2,5,3}的LCS={1,2,5}, d p [ i ] [ j ] dp[i][j] 表示子序列 a i a_i b j b_j 的最长公共子序列长度,当 a i = b j a_i = b_j 时,找到 a i 1 a_{i-1} b j 1 b_{j-1} 的最长公共子序列,然后在其尾部加上 a i a_i 即可得到a和b的LCS;当 a i b j a_i \neq b_j 时,求解两个子问题:① a i 1 a_{i-1} b j b_j 的LCS,② a i a_i b j 1 b_{j-1} 的LCS,然后取最大值,复杂度为 O ( n m ) O(nm)

d p [ i ] [ j ] = { d p [ i 1 ] [ j 1 ] + 1 a i = b j max ( d p [ i 1 ] [ j ] , d p [ i ] [ j 1 ] ) a i b j dp[i][j]= \begin{cases} dp[i-1][j-1]+1& {a_{i}=b_{j}} \\ \max(dp[i-1][j],dp[i][j-1])& {a_{i} \neq b_{j}} \end{cases}

HDU-1159 Common Subsequence

Problem Description
A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = <x1, x2, …, xm> another sequence Z = <z1, z2, …, zk> is a subsequence of X if there exists a strictly increasing sequence <i1, i2, …, ik> of indices of X such that for all j = 1,2,…,k, xij = zj. For example, Z = <a, b, f, c> is a subsequence of X = <a, b, c, f, b, c> with index sequence <1, 2, 4, 6>. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
The program input is from a text file. Each data set in the file contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct. For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.
Sample Input
abcfbc abfcab
programming contest
abcd mnp
Sample Output
4
2
0

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1003;
char a[maxn], b[maxn];
int dp[maxn][maxn];
int main() {
    while (~scanf("%s%s", a + 1, b + 1)) {
        memset(dp, 0, sizeof(dp));
        int len1 = strlen(a + 1);
        int len2 = strlen(b + 1);
        for (int i = 1; i <= len1; i++)
            for (int j = 1; j <= len2; j++)
                if (a[i] == b[j])dp[i][j] = dp[i - 1][j - 1] + 1;
                else dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
        printf("%d\n", dp[len1][len2]);
    }
    return 0;
}
//滚动数组版后文有

L I S LIS

LIS(Longset Increasing Subsequence)最长递增子序列。给定一长度为n的数组,找出一个最长的单调递增子序列。
例如A={5,6,7,4,2,8,3}的最长递增子序列是{5,6,7,8},长度为4。
法一
借助前面讲的LCS,对数组A排序后得到A’,那么A的LIS就是A和A’的LCS,其复杂度是 O ( n 2 ) O(n^2)

法二
定义dp[i]表示以第i个数结尾的最长递增子序列长度,那么
d p [ i ] = m a x ( d p [ j ] + 1 0 ) 0 < j < i , A j   < A i dp[i]=max(dp[j]+1,0),0<j<i,A_j~<A_i
最后答案是max{dp[i]},其复杂度也是 O ( n 2 ) O(n^2)

法三
其实不算DP算法,转换思维通过辅助数组统计LIS的长度,其复杂度是 O ( n l o g ( n ) ) O(nlog(n))
定义:辅助数组 d [ ] d[] ,原序列 h i g h [ ] high[] l e n len =数组 d d 内数据个数
初始化: d [ 1 ] = h i g h [ 1 ] l e n = 1 d[1]=high[1],len=1
步骤:遍历 h i g h [ ] high[] ,①如果 h i g h [ k ] > d [ ] high[k]>d[] 末尾数字,就加到 d d 后面;②否则替换 d [ ] d[] 中第一个大于它的数字。
比如 h i g h [ ] high[] ={1,5,6,2,3,4}

high[] d[] len
1 5 6 2 3 4 1 1
1 5 6 2 3 4 1 5 2
1 5 6 2 3 4 1 5 6 3
1 5 6 2 3 4 1 2 6 3
1 5 6 2 3 4 1 2 3 3
1 5 6 2 3 4 1 2 3 4 4

结束后len=4,即LIS长度为4。

HDU-1257 最少拦截系统

Problem Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
Input
输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
Output
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
Sample Input
8 389 207 155 300 299 170 158 65
Sample Output
2

#include<bits/stdc++.h>
using namespace std;
const int maxn = 10004;
int n, high[maxn];
int LIS1() {  //法一
    int high2[maxn],dp[2][maxn] = { 0 };//滚动数组
    memcpy(high2, high, sizeof(high));
    sort(high2 + 1, high2 + n + 1);
    for (int i = 1; i <= n; i++) //LCS
        for (int j = 1; j <= n; j++)
            if (high[i] == high2[j])
                dp[i & 1][j] = dp[i & 1 ? 0 : 1][j - 1] + 1;
            else
                dp[i & 1][j] = max(dp[i & 1 ? 0 : 1][j], dp[i & 1][j - 1]);
    return dp[n&1][n];
}
int LIS2() {  //法二
    int dp[maxn], ans = 1;
    for (int i = 1; i <= n; i++) {
        dp[i] = 1;
        for (int j = 1; j < i; j++)
            if (high[j] < high[i])
                dp[i] = max(dp[j] + 1, dp[i]);
        ans = max(ans, dp[i]);
    }
    return ans;
}
int LIS3() {  //法三
    int d[maxn], len = 1;
    d[1] = high[1];
    for (int i = 2; i <= n; i++)
        if (high[i] > d[len])
            d[++len] = high[i];
        else {  //用lower_bound()查询第一个>high[i]的地址
            int j = lower_bound(d + 1, d + len + 1, high[i]) - d;
            d[j] = high[i];
        }
    return len;
}
int main() {
    while (cin>>n) {
        for (int i = 1; i <= n; i++)
            cin >> high[i];
        //cout << LIS1() << "\n";
        //cout << LIS2() << "\n";
        cout << LIS3() << "\n";
    }
    return 0;
}

原创不易,请支持正版。(百度发现我的好多博客被抄袭qswl
博主首页:https://blog.csdn.net/qq_45034708
你的点赞将会是我最大的动力,关注一波

猜你喜欢

转载自blog.csdn.net/qq_45034708/article/details/107644351
今日推荐