HJ24 合唱队
描述
N 位同学站成一排,音乐老师要请最少的同学出列,使得剩下的 K 位同学排成合唱队形。
设位同学从左到右依次编号为 1,2…,K ,他们的身高分别为
,若存在
使得
且
,则称这
名同学排成了合唱队形。
通俗来说,能找到一个同学,他的两边的同学身高都依次严格降低的队形就是合唱队形。
例子:
123 124 125 123 121 是一个合唱队形
123 123 124 122不是合唱队形,因为前两名同学身高相等,不符合要求
123 122 121 122不是合唱队形,因为找不到一个同学,他的两侧同学身高递减。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
注意:不允许改变队列元素的先后顺序 且 不要求最高同学左右人数必须相等
数据范围:
输入描述:
用例两行数据,第一行是同学的总数 N ,第二行是 N 位同学的身高,以空格隔开
输出描述:
最少需要几位同学出列
示例1
输入:
8
186 186 150 200 160 130 197 200
复制
输出:
4
说明:
由于不允许改变队列元素的先后顺序,所以最终剩下的队列应该为186 200 160 130或150 200 160 130
题解
动态规划解法
思路:
根据题意,假设我们选取索引为i的同学作为中间人选,则只需要求出[0,i-1]的最长递增序列,以及[i+1,n-1]的最长递减序列即可知道最少的出队次数。
步骤:
- 使用inc_dp[i]表示从左到右遍历时以索引i结束时的最长递增序列长度
- 使用dec_dp[i]表示从左到右遍历时以索引i结束时的最长递减序列长度
- inc_dp初始条件:inc_dp[0] = 1,表示只有一个元素
- 推导条件:dp[i] = max(dp[i],dp[k] + 1),其中k是[0,i-1]范围内的元素,且height[k] < height[i],表示取前[0,k]个元素作为递增序列的时候,再加上第i个元素能组成的递增序列长度
- dec_dp的初始化、推导条件和inc_dp类似
- 求解:遍历整个数组,以索引i为中心时需要出队的长度len = height.size() - (inc_dp[i]+dec_dp[i]-1),也就是总人数减去以i为中心索引时左边的递增长度、右边的递减长度
代码如下:
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
int solve(const std::vector<int> &v)
{
std::vector<int> inc_dp(v.size(),1);
std::vector<int> dec_dp(v.size(),1);
for (int i = 1;i < v.size();++i)
{
for (int k = i - 1;k >= 0;--k)
{
if (v[i] > v[k])
{
inc_dp[i] = std::max(inc_dp[k]+1,inc_dp[i]);
}
else if (v[i] == v[k])
{
inc_dp[i] = std::max(inc_dp[i],inc_dp[k]);
}
}
}
for (int i = v.size() - 1;i > 0;--i)
{
for (int k = i + 1;k < v.size();++k)
{
if (v[i] > v[k])
{
dec_dp[i] = std::max(dec_dp[k]+1,dec_dp[i]);
}
else if (v[i] == v[k])
{
dec_dp[i] = std::max(dec_dp[i],dec_dp[k]);
}
}
}
int len = 0;
for (int i = 0;i < v.size();++i)
{
len = std::max(len,(inc_dp[i] + dec_dp[i] - 1));
}
return v.size() - len;
}
int main()
{
int n;
std::cin >> n;
std::vector<int> v(n, 0);
for (int i = 0; i < n; ++i)
{
std::cin >> v[i];
}
std::cout << solve(v) << std::endl;
return 0;
}