最长递增子序列(LIS)
O(n2 O ( n 2 )算法
设a[i]为给定序列的第i个元素的值,dp[i]表示以第i个元素结尾的最长递增子序列长度
– 状态转移方程:
dp[i] = max{dp[i], dp[j] + 1} (a[i] > a[j] && i > j)
- O(n2 O ( n 2 )代码模板
for (i=1; i<=n; ++i)
dp[i] = 1;
for (i=1; i<=n; ++i)
for (j=1; j<i; ++j)
if (a[i] > a[j])
dp[i] = max(dp[i], dp[j]+1);
O(nlogn) O ( n l o g n ) 算法
a[i]为给定序列的第i个元素的值,dp[i]存放以i为结尾最长递增子序列的长度,B[i]表示最长序列长度为len时可以构成最长长度的第i位的最小值
O(nlong) O ( n l o n g ) 代码模板
B[1] = a[1];
len = 1; //存最长序列长度
dp[1] = len;
for(int i=2; i<=n; i++)
{
if (a[i] > B[len]) //直接插入序列最后
{
len++;
dp[i] = len;
B[len] = a[i];
}
else
{
int p = lower_bound(B+1, B+len+1, a[i]) - B;
//为了使B序列潜力最大,所以用小的去替换大的序列元素
dp[i] = p;
B[p] = a[i];
}
}
最长递增子序列(LCS)
O(n2) O ( n 2 ) 算法
定义:给定两个序列,求他们相同的子序列中最长的长度。
x[1-n], y[1-m], dp[i][j]表示x从1到i,y从1到j的相同子序列最长长度
memset(dp, 0, sizeof(dp));
for (i=1; i<=n; ++i)
for (j=1; j<=m; ++j)
{ if (x[i] == y[j]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = max(dp[i-1][j], dp[i][j-1]); }
O(nlogn) O ( n l o g n ) 算法
最长公共子序列 的 nlogn 的算法本质是 将该问题转化成 最长增序列(LIS),因为 LIS 可以用nlogn实现,所以求LCS的时间复杂度降低为 nlogn。
证明
参考
– 转化:将LCS问题转化成LIS问题。
假设有两个序列 s1[ 1~6 ] = { a, b, c , a, d, c }, s2[ 1~7 ] = { c, a, b, e, d, a, b }。记录 s1 中每个元素在 s2 中出现的位置, 再将位置按降序排列, 则上面的例子可表示为:loc( a)= { 6, 2 }, loc( b ) = { 7, 3 }, loc( c ) = { 1 }, loc( d ) = { 5 }。将s1中每个元素的位置按s1中元素的顺序排列成一个序列s3 = { 6, 2, 7, 3, 1, 6, 2, 5, 1 }。在对s3求LIS得到的值即为求LCS的答案
- 参考代码原地址(记得这边要补上)
最长公共子串(子串必须连续)
- 代码
memset(dp, 0, sizeof(dp));
for (i=1; i<=n; ++i)
for (j=1; j<=m; ++j)
{ if (x[i] == y[j]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = 0; }
最大连续子序列和
给定一个整数序列A0,A1,…,An-1。求最大连续子序列的和。为了方便,若所有数都为负数,输出0。(不是很懂这里什么意思,按理说应该是遍历一遍取最大的那个负数就可以了)
a[1-n]表示数组,dp[i]表示以i为结尾的最大连续子序列和
dp[0] = 0;
for (int i=1; i<=n; ++i)
if (dp[i-1] > 0)
dp[i] = dp[i-1] + a[i];
else
dp[i] = a[i];
例题
- 题目大意:给出一个数字串 A1 ~ An,你可以选择一个连续的子串 [l, r],使得里面所有的数字 Ai 的值变成 f(Ai) = (1890 * Ai + 143) % 10007,或者你也可以不要选择。最后要求所有数字的和最大是多少。
- 分析:最大连续子段和。要使得最后的数字和最大,那么就是求一个区间 [l, r] 使得增量最大,也就是说使得 Σ (f(Ai) – Ai) 最大,那么很明显就转换成了最大连续子段和问题了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=100005;
const int mod=10007;
long long int a[maxn],f[maxn],dp[maxn];
int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
long long ans=0;
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
f[i] = (1890 * a[i] + 143) % mod - a[i];
ans += a[i];
}
dp[0] = 0;
int res = 0;
for (int i=1; i<=n; i++)
{
if (dp[i-1] > 0)
dp[i] = dp[i-1] + f[i];
else
dp[i] = f[i];
if (dp[i] > res)
res = dp[i];
}
printf("%lld\n", ans+res);
}
return 0;
}
基础练习:
参考文献:酷酷学姐的PPT