题解:登山
描述
五一到了,PKU-ACM队组织大家去登山观光,队员们发现山上一个有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就 不再向上走了。队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?
输入
Line 1: N (2 <= N <= 1000) 景点数
Line 2: N个整数,每个景点的海拔
输出
最多能浏览的景点数
样例输入
8
186 186 150 200 160 130 197 220
样例输出
4
解析
这道题是一个最长上升子序列问题,我们可以正着求一遍最长上升子序列,然后反着求一遍最长上升子序列,最后的最大值是两者的和减一,值得注意的是因为中间包含了两个最高点,所以要减一,同时这个用n2的dp存每一个点的最大序列,因为最后结果不一定是两个绝对最长的,所以要把每一个都比比较变(我感觉就像枚举中间最高点一样)
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001],b[1000001],c[1000001],ans = 0;
int main()
{
scanf("%d" ,&n);
for(int i = 1;i <= n; i++){
scanf("%d" ,&c[i]);
}
for(int i = 1;i <= n; i++){
a[i] = 1;
for(int j = 1;j < i; j++){
if(c[j] < c[i]){
a[i] = max(a[i],a[j] + 1);
}
}
}
for(int i = n;i >= 1; i--){
b[i] = 1;
for(int j = n;j > i; j--){
if(c[j] < c[i]){
b[i] = max(b[i],b[j] + 1);
}
}
}
for(int i = 1;i <= n; i++){
ans = max(ans,a[i] + b[i] - 1);
}
printf("%d" ,n - ans);
return 0;
}
补充
同时,这里还有一种队列优化的nlogn做法,基本思路和n2的算法类似,但是采用了队列的方法优化查找最大序列的过程(优化的算法不再赘述),值得一提的是,这里一定要在循环中把每一次取得的最大值存进去而不能只管最后的值,否则会出错
代码如下
#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001];
int x[100001];
int y[100001];
int te[100001];
int ans = 0;
int ed = 0;
int main() {
scanf("%d" ,&n);
for(int i = 1; i <= n; i++) {
scanf("%d" ,&a[i]);
}
memset(te,0,sizeof(te));
for(int i = 1; i <= n; i++) {
if(a[i] > te[ed]) {
ed++;
te[ed] = a[i];
}
if(a[i] < te[ed]) {
int pl = lower_bound(te + 1,te + 1 + ed,a[i]) - te;
te[pl] = a[i];
}
x[i] = ed;
}
ed = 0;
memset(te,0,sizeof(te));
for(int i = n; i >= 1; i--) {
if(a[i] > te[ed]) {
ed++;
te[ed] = a[i];
}
if(a[i] < te[ed]) {
int pl = lower_bound(te + 1,te + 1 + ed,a[i]) - te;
te[pl] = a[i];
}
y[i] = ed;
}
for(int i = 1;i <= n; i++){
ans = max(x[i] + y[i] - 1,ans);
}
printf("%d" ,n - ans);
return 0;
}

浙公网安备 33010602011771号