计算最少出列多少位同学,使得剩下的同学排成合唱队形
说明:
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1<=i<=K)使得T1<T2<......<Ti-1<Ti>Ti+1>......>TK。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
注意不允许改变队列元素的先后顺序
请注意处理多组输入输出!
实际就是,从队伍里直接下来人,让剩下的人刚好满足条件的情况。
8个人,[186,186,150,200,160,130,197,200]
当以每位同学为最高点时,进行判断,从他左边找比他矮的同学(可以多个,且要为递增序列)。再从右边来找比他高的同学。
例如:
先找左边的
我们假设第一位身高为 186 的同学,为最高点(Ti),他左边没有其他人,所以我们设定左边人数为0
第二位身高为 186 的同学,左边只有一个186,和他一样,所以他左边也为0
第三个150,左边也为0
第四个200,左边有186和150的同学,都比他矮,但是这两者不成递增顺序,所以只能留一个,即左边为1
第五个160,左边有150的同学,所以为1
第六个130.左边没有比他矮的了,所以为0
第七个197.左边比他矮的有186,150,160,130,而成递增顺序的为[150,160],故左边为2
第八个200.左边比他矮的有186,150,160,130,197,而成递增顺序的为[150,160,197],左边为3.
综上,从左边找的时候,我们得到的人数表为 00011023
同理,可得从右边找的,我们得到的人数表为 22121000
此时,我们得到的两个序列是啥意思呢??
其实就是当我们将每一个同学假设为最高位时,他左右两边会出现的递增序列和低贱序列。
我们如果将对应的左右两边的人数加起来,在加上i同学自身,我们就可以得到当每一位同学为最高时,所具有的人数列表。同时,用总人数减去这个列表,我们也就得到了每一位同学为最高时,所减少的人数。
1、动态规划
import numpy as np
def left_max(N,H):
l = np.zeros(N , dtype=np.int32)
for i in range(N):
for j in range(i):
#需要两个循环,使得能对i前所有数据进行判断
if H[i] > H[j] and l[j] + 1 >l[i]:
#这里的这个条件为
# 1、找比当前H[i] 唉的同学。
# 2、如果dp上有值,表明这个数前面是有比他还要小的数存在,且形成了递增序列。
#只有两个条件都满足才有意义。
l[i] += 1
return l
def right_min(N,H):#找右边最大的
r = np.zeros(N, dtype=np.int32)
for i in range(N - 1,-1,-1):
for j in range(N - 1, i , -1):
if H[i] > H[j] and r[j] + 1 > r[i]:
r[i] += 1
return r
N = 8
H = [186,186,150,200,160,130,197,200]
sum = []
left = left_max(N,H )
right = right_min(N,H)
for i in range(N):
sum.append(left[i]+right[i]+1)
print(N - max(sum))
2、分治法
其逻辑其实与上述讲解 的一样,也是从左边找递增,右边找递减。但是分治法,将左右两边变成了两个相互独立的问题。只需要一个找递增的函数即可,另一边可以看错颠倒顺序后找递增序列。
加和的时候要注意一下,要一一对应!!
#分治法:
import bisect
def max_order(H):
H_num = [] #用于排序,
H_max = [] #每个元素右边的最大数(包括元素自己)
for i in H:
local = bisect.bisect_left(H_num, i)
#bisect是python的内涵库,用于管理列表的插入和排序
#bisect_left是插入H_num左边,默认从0开始。
if local == len(H_num):
H_num.append(i)
H_max.append(local+1)
else:
H_num[local] = i
H_max.append(local+1)
return H_max
while True:
N = 8
H_int = [186, 186, 150, 200, 160, 130, 197, 200]
left = max_order(H_int)
right = max_order(H_int[::-1])[::-1]
sum_s = []
for i in range(len(left)):
# left_s[i]+right_s[i]-1表示此人是中间位置的人时,合唱队的人数
sum_s.append(left[i]+right[i]-1)
print('最少要出列',str(N-max(sum_s)),'位同学')
break