[NOIP2004 提高组] 合唱队形
难度:普及/提高-
解题思路
这题与最长上升子序列相似
但是题意要求的数列为一个类似山峰的队列
接下来我们仔细分析题目
t1 < t2 <t3 < ... < ti > ti+1 > ti+2 > ... >tk
发现了什么
题意要求的数列可以拆分成一个最长上升子序列和一个最长下降子序列
那么只要枚举峰顶所在的位置就行了
最长上升子序列
最长上升子序列可以用二分或动规求
二分:O(nlog2n)
动规:O(n^2)
可以看出二分更快
但是题目枚举需要求出所有项到第一项的最长上升子序列
二分需要一一计算
而动规不需要
所以这道题使用动规更快
二分:O(n^2log2n)
动规:O(n^2)
此处提供动规最长上升子序列的模板
#include<bits/stdc++.h>
using namespace std;
int n,a[1002],f[1002];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i]=1; //以第i个数为末尾的上升序列最初长度为1
}
for(int i=1;i<=n;i++) //枚举i的位置
for(int j=1;j<i;j++) //在i的前面找j的位置
if(a[i]>a[j]) //如果满足条件,则第i个数可以放在j后边
f[i]=max(f[j]+1,f[i]); //取较大的一种再放
printf("%d",*max_element(f+1,f+n+1)); //从 F[1]到F[n] 找最大值
return 0;
}
解题代码
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n,a[N],f1[N],f2[N],ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f1[i]=1;
f2[i]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
if(a[i]>a[j])
f1[i]=max(f1[j]+1,f1[i]);
for(int i=n;i>=1;i--)
for(int j=n;j>i;j--)
if(a[i]>a[j])
f2[i]=max(f2[j]+1,f2[i]);
for(int i=1;i<=n;i++){
ans=max(ans,f1[i]+f2[i]-1);
}
printf("%d",n-ans);
return 0;
}