动态规划-LCS、LIS

159 阅读4分钟

文章目录

L C S LCS 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] dp[i][j]表示子序列 a i a_i ai​和 b j b_j bj​的最长公共子序列长度,当 a i = b j a_i = b_j ai​=bj​时,找到 a i − 1 a_{i-1} ai−1​和 b j − 1 b_{j-1} bj−1​的最长公共子序列,然后在其尾部加上 a i a_i ai​即可得到a和b的LCS;当 a i ≠ b j a_i \neq b_j ai​​=bj​时,求解两个子问题:① a i − 1 a_{i-1} ai−1​和 b j b_j bj​的LCS,② a i a_i ai​和 b j − 1 b_{j-1} bj−1​的LCS,然后取最大值,复杂度为 O ( n m ) O(nm) 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} dp[i][j]={dp[i−1][j−1]+1max(dp[i−1][j],dp[i][j−1])​ai​=bj​ai​​=bj​​

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


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) O(n2)。

法二
定义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 dp[i]=max(dp[j]+1,0),0<j<i,Aj​ <Ai​
最后答案是max{dp[i]},其复杂度也是 O ( n 2 ) O(n^2) O(n2)。

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

high[]d[]len
1 5 6 2 3 411
1 5 6 2 3 41 52
1 5 6 2 3 41 5 63
1 5 6 2 3 41 2 63
1 5 6 2 3 41 2 33
1 5 6 2 3 41 2 3 44

结束后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;
}

原创不易,请勿转载本不富裕的访问量雪上加霜
博主首页:blog.csdn.net/qq_45034708
如果文章对你有帮助,记得关注点赞收藏❤