HJ24 合唱队

其实和题解一样只是自己想记录一下自己的理解、

描述

N 位同学站成一排,音乐老师要请最少的同学出列,使得剩下的 K 位同学排成合唱队形。

设KK位同学从左到右依次编号为 1,2…,K ,他们的身高分别为T_1,T_2,…,T_KT1​,T2​,…,TK​ ,若存在i(1\leq i\leq K)i(1≤i≤K) 使得T_1<T_2<......<T_{i-1}<T_iT1​<T2​<......<Ti−1​<Ti​ 且 T_i>T_{i+1}>......>T_KTi​>Ti+1​>......>TK​,则称这KK名同学排成了合唱队形。

通俗来说,能找到一个同学,他的两边的同学身高都依次严格降低的队形就是合唱队形。

例子:

123 124 125 123 121 是一个合唱队形

123 123 124 122不是合唱队形,因为前两名同学身高相等,不符合要求

123 122 121 122不是合唱队形,因为找不到一个同学,他的两侧同学身高递减。

你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

注意:不允许改变队列元素的先后顺序 不要求最高同学左右人数必须相等

数据范围: 1≤n≤3000

输入描述:

用例两行数据,第一行是同学的总数 N ,第二行是 N 位同学的身高,以空格隔开

输出描述:

最少需要几位同学出列

分析:

dp[i]变量代表着以i为中心人物的向左的最大递减序列长度加上向右的最大递减序列-1,由于无论是递增序列还是递减序列都会把这个中心人物记录一次所以重复了一次需要减掉。那么可以拆成两个问题。

1.从左到 i 的最大递增子序列长度

2.从 i 到右的最大递减子序列长度

这两个都是很简答的DP问题

#include<iostream>
#include<vector>
using namespace std;
int nums[3010];
int main()
{
	int n, i, j;
	cin >> n;
	//变量dp[i]代表着以i作为中间最高的那个人,存的值为最大队伍长度
	//换个说法就是以i作为中间,向左看向右看都是最大递减子序列,那么也就是从队伍最左侧到这个i的最大递增子序列,从这里到右侧的最大递减子序列
	for (i = 0; i < n; i++)
	{
		cin >> nums[i];
	}
	//我们先求从左向右的最大递增子序列长度
	vector<int> dp1(n,1);
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < i; j++)
		{
			if (nums[i] > nums[j])
			{
				dp1[i] = max(dp1[i], dp1[j] + 1);
			}
		}
	}
	//这里再求从左向右的最大递减子序列
	vector<int> dp2(n, 1);
	for (i = n - 1; i > -1; i--)
	{
		for (j = n - 1; j > i ; j--)
		{
			if (nums[i] > nums[j])
			{
				dp2[i] = max(dp2[i], dp2[j] + 1);
			}
		}
	}
	//最后由于两个子序列的最大长度叠加起来会把自己本身算两次,需要减一,我们在这些子序列最大长度之和-1中找到其中最大的
	int maxum = 0;
	for (i = 0; i < n; i++)
	{
		maxum = max(dp1[i] + dp2[i] - 1, maxum);
	}
	cout <<n- maxum << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值