线性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]} 
posted @ 2021-01-27 22:16  Paranoid5  阅读(78)  评论(0编辑  收藏  举报