题面:
![](https://i-blog.csdnimg.cn/blog_migrate/ce91de34fd48b53cb415b950bbce0b96.png)
解题:
数据范围n≤100非常善良~这意味着我们足以运行时间复杂度O(n²)甚至O(n³)的算法。
由于题目要求身高不能相等,我们不妨将相邻的同身高的两个同学先释放掉一个……
cin >> t[1];
for (int i = 2; i <= n; i++)
{
int temp; cin >> temp;
if (temp == t[i - 1]) { continue; } //除去相邻同身高的人
t[++tLen] = temp;
}
算法分析:
据题意,一定有一位最高的同学Ti,其前、后同学身高依次递减……
由于数据范围较小,我们不妨试着求出每个同学作为Ti时需要除去的人数,再取最小值。
因此,问题便可以转化为:求从Ti出发的左侧最长下降子链、右侧最长下降子链长度之和
DP求最长下降子链长度
例如:对于4 、1 、2 、4 、5 、1这样一组数据,我们用down[ i ]储存第1~第i个数的子链相关数据,len=1为最长下降子链长,则down[1]=t[1]=4。遍历后面的第2~6号数,以下模拟dp过程:
对于t[2]=1:比down[len]=down[1]=4小,追加,即down[++len]=t[2]=1 ; [4,1]
对于t[3]=2:比down[len]=down[2]=1大,不追加;
但比down[2]=1大,比down[1]=4小,更具潜力,将1替换成2:down[2]=2,[4,2]
对于t[4]=4:比down[len]=2大,不追加; 和4等大,不替换; [4,2]
对于t[5]=5:比起始元素4都大,直接跳过; [4,2]
对于t[6]=1:比down[len]=2小,down[++len]=t[6]=1; [4,2,1]
至此,我们得到了最长递减子链长度:len=3,
若无法说服自己可以动笔算算,详见getRDownLen代码:
int getRDownLen(int left, int right) //获取从t[left]右侧的最长下降子链
{
int down[105];
int len = 1;
down[1] = t[left];
for (int i = left + 1; i <= right; i++)
{
if (t[i] < down[len]) //比队尾元素更小,直接加入队列末端
{
down[++len] = t[i];
continue;
}
for (int j = len; j >= 2; j--) //从len开始向前枚举
if (t[i] > down[j] && t[i] < down[j - 1]) //比down[j]大且比down[j-1]小,则覆盖down[j]
down[j] = t[i];
}
return len - 1; //输出不算自身
}
AC代码奉上
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1e6 + 5;
int n, t[105], ans = MAXN, tLen = 1;
int getLDownLen(int left, int right) //获取从t[left]左侧的最长下降子链
{
int down[105];
int len = 1;
down[1] = t[right];
for (int i = right - 1; i >= left; i--)
{
if (t[i] < down[len]) //比队尾元素更小,直接加入队列末端
{
down[++len] = t[i];
continue;
}
for (int j = len; j >= 2; j--) //从len开始向前枚举
if (t[i] > down[j] && t[i] < down[j - 1]) //比down[j]大且比down[j-1]小,则覆盖down[j]
down[j] = t[i];
}
return len - 1; //输出不算自身
}
int getRDownLen(int left, int right) //获取从t[left]右侧的最长下降子链
{
int down[105];
int len = 1;
down[1] = t[left];
for (int i = left + 1; i <= right; i++)
{
if (t[i] < down[len]) //比队尾元素更小,直接加入队列末端
{
down[++len] = t[i];
continue;
}
for (int j = len; j >= 2; j--) //从len开始向前枚举
if (t[i] > down[j] && t[i] < down[j - 1]) //比down[j]大且比down[j-1]小,则覆盖down[j]
down[j] = t[i];
}
return len - 1; //输出不算自身
}
int main()
{
cin >> n;
if (n == 2) { cout << "0" << endl; return 0; }
cin >> t[1];
for (int i = 2; i <= n; i++)
{
int temp; cin >> temp;
if (temp == t[i - 1]) { continue; } //除去相邻同身高的人
t[++tLen] = temp;
}
for (int i = 1; i <= tLen; i++)
{
int len1 = getLDownLen(1, i);
int len2 = getRDownLen(i, tLen);
int addLen = len1 + len2 + 1; //总剩余队列长度
ans = min(ans, n - addLen);
}
cout << ans << endl;
return 0;
}