Bootstrap

华为笔试-超简单看懂合唱队问题

合唱队

问题描述:

计算最少出列多少位同学,使得剩下的同学排成合唱队形。

说明:

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位

同学排成合唱队形。 
合唱队形是指这样的一种队形:

设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,   

则他们的身高满足存在i(1<=i<=K)

使得T1<T2<......<Ti-1< Ti >Ti+1>......>TK。 
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

 

输入描述:

 

整数N

输出描述:

 

最少需要几位同学出列

示例1

输入

复制

8
186 186 150 200 160 130 197 200

输出

复制

4

 

 

问题解析

算法原理:动态规划

涉及概念:最长递增子序列

解题思路 :在N位同学中找出一个同学i,经过去除几位同学后,使得i为一个分割点,使得i之前的同学身高都小于i,i之后的同学身高都大于i。

若使去除的同学最少,则留下的同学应最大。

解题说明:

取示例 A = {186, 186, 150, 200, 160, 130, 197, 200}

(1)首先对A中所有的元素,计算每个元素在其正向最长递增子序列中的位置:

例如A[4] = 160,其所在最长子序列为{150,160,197,200},故所在位置为2。

最长子序列计算方法见上篇文章:超简单看明白如何求最长子序列-动态规划

计算结果如下:

 

i01234567
A[i]186186150200160130197200
位置11122134

(2)对A中的所有元素,计算每个数在反向递增子序列中的位置,首先对A进行逆序排列,再计算,

计算结果如下:

 

i01234567
A[i]186186150200160130197200
位置33232111

(3)对于A中每个元素i:

计算其位于正向递增子序列中的位置,则表示位于i前面,小于A[i]且保持递增的有多少个元素;

计算其位于反射递增子序列中的位置,则表示位于i后面,大于A[i]且保持递减的有多少个元素;

将每个元素所在正向递增子序列中的位置+所在反向递减子序列中的位置相加,取最大者对应的下标i,即为所求的分界点i。

计算结果如下:

 

i01234567
A[i]186186150200160130197200
正向位置11122134
反向位置33232111
正向累加44354245

取最大值i = 4,此时该元素所在队列的长度即为 5 - 1 = 4。

减1说明:

由于正反累加时,当前元素计算2次,例如当i = 4时,对于160这个数,

                            所在递增序列为{150,160,,,},位置为2

                            所在反向递增序列为{130,160,,,},位置为2

                            若取i = 4为分界点,则160所在的队列为{150,160,130},队列长度为 2 + 2 - 1 = 3。

(4)因此需要出队的人数 = 总人数 - 队列长度

针对本示例,应为 8 - (5 - 1) = 4。

代码如下:

int main()
{
	
	int n = 0;
	while(cin>>n)
	{
		int m = 0;
		vector<int> dp1(n,1);//记录正向位置
		vector<int> dp2(n,1);//记录反向位置
		vector<int> vecints;
		for(unsigned int i = 0; i < n; i++)
		{
			cin >> m;
			vecints.push_back(m)
		}
		//计算正向位置
		for(unsigned int i = 0; i < n; i++)
		{
			for(unsigned int j = 0; j < i; j++)
			{
				if(vecints[j] < vecints[i] && dp1[j] >= dp1[i])
					dp1[i] = dp1[j] + 1;
			}
		}
		//反转序列
		reverse(vecints.begin(),vecints.end());
		//计算反向位置
		for(unsigned int i = 0; i < n; i++)
		{
			for(unsigned int j = 0; j < i; j++)
			{
				if(vecints[j] < vecints[i] && dp2[j] >= dp2[i])
					dp2[i] = dp2[j] + 1;
			}
		}
		//反转位置
		reverse(dp2.begin(),dp2.end());
		int Max = -1;//记录最大数列
		for (unsigned int i = 0; i < n; i++)
		{
			Max = Max < dp1[i] + dp2[i] ? dp1[i] + dp2[i] : Max;
		}
		cout << n - Max + 1 << endl;
	}
	return 0;
}

;