线性DP(1)
线性DP(1)
1.LIS
问题:给定一个长度为n的数组,找出一个最长的单调递增子序列。
例如:{5,6,7,4,2,8,3}最长的是{5,6,7,8},长度为n。
例题:
HDU - 1257
该题就是给出一个序列,然后求最少可以有多少个单调下降的序列。
每一个下降序列都有一个最小值Ai,每一个Ai+1是必须大于Ai的。
这里可以使用反证法证明。(其实仔细想一下是很显然的)
这样的话就得出了一个序列A,序列的长度就是需要输出的答案。这个序列是上升的。
并且这个序列就是原序列中的LIS。
同样的反证法可以证明。
这似乎还是个什么鬼定律
开始用DP思想来求解LIS:
定义s[i]为序列的第i个字符;
定义D[i]为前i个之前的序列的最长上升子序列的长度。
于是就有:
for j : 0<j<i
a[i] > a[j] : d[i] = max{d[j}+1
显然,我们的答案是最大的 d[i]。
树状数组优化
在之前的求解过程中内层循环是为了找到满足条件的最大值。那么有什么来进行加速呢?
我们都知道树状数组可以来维护前缀和的最大值和最小值。
所以考虑使用树状数组进行这个神奇的操作。
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) x&-x
#define ll long long
#define For(i,a,n) for(int i = a;i <= n;i++)
const int N =1e6+50;
int a[N];
int t = 0,ans1 = 0,ans2 = 0,m = 0;
int tree1[N] = {0},tree2[N] = {0};
void add1(int x,int k){
while(x > 0){
tree1[x] = max(tree1[x],k);
x-=lowbit(x);
}
}
int sum1(int x){
int res = 0;
while(x <= m){
res = max(res,tree1[x]);
x+=lowbit(x);
}
return res;
}
void add2(int x,int k){
while(x <= m){
tree2[x] = max(tree2[x],k);
x+=lowbit(x);
}
}
int sum2(int x){
int res = 0;
while(x > 0){
res = max(res,tree2[x]);
x-=lowbit(x);
}
return res;
}
int main() {
while(cin>>a[++t]){
m = max(a[t],m);
}
For(i,1,t-1){
int p1 = sum1(a[i]),p2 = sum2(a[i]);
ans1 = max(ans1,p1+1),ans2 = max(ans2,p2+1);
add1(a[i],p1+1),add2(a[i]+1,p2+1);
}
cout<<ans1<<endl<<ans2<<endl;
return 0;
}
单调栈似乎也可以优化到nlonn的复杂度
(这题还可以使用贪心+二分也可以有nlogn的复杂度)
### 2.LCS
** 问题 **:给定两个序列X和Y,找出X和Y的一个最长公共子序列
[HDU-1159](http://acm.hdu.edu.cn/showproblem.php?pid=1159)
这个问题是纯裸的,没什么分析。
直接写DP思路。
定义DP[i][j]为X长度为i,Y长度为j时的LCS。
x[i] == y[j] : DP[i][j] = DP[i-1][j-1]+1
x[i] != y[j]:DP[i][j] = max{DP[i-1][j],DP[i][j-1]}