LIS 最长上升子序列
子序列:字符序列的子序列是只从给定字符序列中随意地不一定连续地去掉若干个字符(也可能不去掉)后所形成的字符序列。
给定一个长度为n的序列:a[0],a[1],a[2]...a[n],求其最长上升子序列的长度。
最长上升子序列:递增的可间断的子序列。
eg: 1, 3, -3, 5, -2, 6,其最长上升子序列为1, 3, 5, 6。
1.O(n²)算法
定义dp[i]:以ai为结尾的最长上升子序列的长度
以ai结尾的上升子序列是:
①只包含ai的子序列
②在满足j<i并且aj<ai的以aj为结尾的上升子列末尾,追加上ai后得到的子序列
综合以上两种情况,便可以得到递推关系式:
dp[i] = max{1, dp[j]+1| j<i且aj<ai}
2.O(nlogn)算法
定义dp[i]:长度为i+1的上升子序列中末尾元素的最小值(不存在就是INF)
最开始全部dp[i]的值都初始化为INF。然后由前到后逐个考虑数列的元素,对于每个aj,如果i=0或者dp[i-1]<aj的话,就用dp[i]=min(dp[i],aj)进行更新。最终找出使得dp[i]<INF的最大的i+1就是结果了。
例题:最少拦截系统
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
Input
输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
Output
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
Sample Input
8 389 207 155 300 299 170 158 65
Sample Output
2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,len;
int a[50010],ans[50010];
int main()
{
while(~scanf("%d",&n)){
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
ans[0]=a[1];
len=0;
for(int i = 2; i <= n; i++){
if(a[i]>ans[len]){
ans[++len]=a[i];
}
else{
int pos = lower_bound(ans,ans+len,a[i])-ans;
ans[pos] = min(ans[pos],a[i]);
}
}
printf("%d\n",len+1);
}
return 0;
}
LCS 最长公共子序列
最长公共子序列问题,即求字符串A,B的公共子序列。
LCS(注意:LCS不一定连续,但和原始序列的元素顺序有关)中长度最长的公共子序列时,因为最长公共子序列不唯一,但是最长公共子序列长度是一定的,所以先把问题简化,如何求两个序列的最长公共子序列长度?
我们首先想到的肯定是暴力枚举法。
先来看看:假设序列A有n 个元素,序列B有 m 个元素,那么A,B分别有2^n,2^m个子序列,如果任意两个子序列一一比较,比较的的子序列高达2^(m+n)对,这还没有算具体的复杂度。
所以我们可以试试动态规划,把这个问题分解成子问题:求A的前i个元素和B的前j个元素之间的最长公共子序列长度。这时的空间复杂度为o(m+n)。
算法思想
定义dp[i][j]:存储字符串A前i位与字符串B前j为的最长公共子序列的长度。
如果a[i]等于b[j]则末尾一定是这个元素
即 dp[i][j]=dp[i-1][j-1]+1;
否则
A去掉a[i]比较剩余的A和B
B去掉b[j]比较A和剩余的B
即 dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
有时题目可能会需要我们输出最长公共子序列,我们可以从末尾往前找,对比a[i]与b[j]是否相等,相等时输出这个元素,不相等时,比较dp[i-1][j]与dp[i][j-1]来寻找剩下的有更长公共子序列的回溯,通过回溯就可以找到一个最长公共子序列。
例题:最长公共子序列LCS
给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的)。
比如两个串为:
abcicba
abdkscab
ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最长的子序列。
Input
第1行:字符串A
第2行:字符串B
(A,B的长度 <= 1000)
Output
输出最长的子序列,如果有多个,随意输出1个。
Sample Input
abcicba
abdkscab
Sample Output
abca
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char str1[1010],str2[1010],ans[1010];
int len1,len2,len3;
int dp[1010][1010];
int main()
{
cin >> str1 >> str2;
len1 = strlen(str1);
len2 = strlen(str2);
memset(dp,0,sizeof(dp));
for(int i = 1; i <= len1; ++i){
for(int j = 1; j <= len2; ++j){
if(str1[i-1] == str2[j-1]){
dp[i][j] = dp[i-1][j-1]+1;
}
else{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
}
len3=0;
while(dp[len1][len2] != 0){
if(str1[len1-1] == str2[len2-1]){
ans[len3++]=str1[len1-1];
len1--;
len2--;
}
else{
if(dp[len1][len2-1]>dp[len1-1][len2]){
len2--;
}
else{
len1--;
}
}
}
for(int i = len3-1; i >= 0; --i){
printf("%c",ans[i]);
}
printf("\n");
return 0;
}