题目大意:
给定一个长度为n整数序列求一个最长子序列,长度为2k+1,要求前k个上升,后k个数下降
分析
对这样一个整数序列分别求最长上升序列和最长下降序列然后对同一点进行相加求最大;
这个题目的重点时LIS问题参考刘汝佳的蓝书
如何求LIS,最笨的方法,我们用d(i)表示从0-i个整数序列的最长上升序列用P数组保存序列
状态转移方程为;
d(i)=max{d(j)|1<j<i且p(j)<p(i)}+1
核心代码
int maxp=0;
for (int i = 0; i < n; i++) {
d[i] = 0;
for (int j = 0; j < i; j++)
if(p[j]<p[i])
d[i] = max(d[i], d[j]+1);
maxp=max(maxp,d[i]);
}
我们可以接着优化
对于两个状态a和b有Pa<Pb且d(a)=d(b)那么在之后的i状态a一定不会比b差-----------如果b满足Pb<Pi,那么a一定满足,反之却不成立,换句话说如果我们只保留a一定不会丢失最优解.
所以对于相同的d值我们只保留P中那最小的一个值 我们用g(i)表示,对于g数组而言,下标表示d值,下标所对应的g值是我们保存 的相同d值中最小的P值;对于g数组而言,元素一定是有序的,所以我们采取二分搜索查下标获取d值
for (int i = 1; i <= n; i++) g[i] = Inf; //别忘了初始化
for (int i = 0; i < n; i++) {
int k = lower_bound(g + 1, g + 1 + n, P[i]) - g;
d[i] = k;
g[k] = P[i];
}
好了,言归正传,说我们这个题目
分析如上,上代码
#include<iostream>
#include<algorithm>
#include<string.h>
#define maxn 10000+10
#define Inf 0x3f3f3f3f
using namespace std;
int n;
int q[maxn], d1[maxn], d2[maxn],g[maxn];
int main()
{
while (cin >> n) {
for (int i = 0; i < n; i++)
cin >> q[i];
memset(g, Inf, sizeof(g));
for (int i = 0; i < n; i++) { //构造递增子序列
int k = lower_bound(g, g + n, q[i]) - g;
d1[i] = k;
g[k] = q[i];
}
memset(g, Inf, sizeof(g));
for (int i = n-1; i >=0; i--) { //构造递减子序列
int k = lower_bound(g, g + n, q[i]) - g;
d2[i] = k;
g[k] = q[i];
}
int ans = 0;
for (int i = 0; i < n; i++) { //求解最大值;
if (d1[i]==d2[i])
{
ans = max(ans, d1[i] + d2[i]+1);
}
}
cout << ans << endl;
}
return 0;
}