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

Posted feengg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了华为笔试-超简单看懂合唱队问题相关的知识,希望对你有一定的参考价值。

合唱队

问题描述:

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

说明:

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;

以上是关于华为笔试-超简单看懂合唱队问题的主要内容,如果未能解决你的问题,请参考以下文章

华为笔试题 合唱队

华为OJ090-合唱队

华为机试题——合唱团

华为机试HJ24:合唱队

#yyds干货盘点# 动态规划专题:合唱队形

题解合唱队形