题目描述
HJ24 合唱队
N 位同学站成一排,音乐老师要请最少的同学出列,使得剩下的 K 位同学排成合唱队形。
设K位同学从左到右依次编号为 1,2…,K ,他们的身高分别为
T
1
,
T
2
,
…
,
T
K
T_1,T_2,…,T_K
T1,T2,…,TK,若存在
i
(
1
≤
i
≤
K
)
i(1\leq i\leq K)
i(1≤i≤K)使得
T
1
<
T
2
<
.
.
.
.
.
.
<
T
i
−
1
<
T
i
T_1<T_2<......<T_{i-1}<T_i
T1<T2<......<Ti−1<Ti且
T
i
>
T
i
+
1
>
.
.
.
.
.
.
>
T
K
T_i>T_{i+1}>......>T_K
Ti>Ti+1>......>TK
则称这KK名同学排成了合唱队形。
通俗来说,能找到一个同学,他的两边的同学身高都依次严格降低的队形就是合唱队形。
例子:
123 124 125 123 121 是一个合唱队形
123 123 124 122不是合唱队形,因为前两名同学身高相等,不符合要求
123 122 121 122不是合唱队形,因为找不到一个同学,他的两侧同学身高递减。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
注意:不允许改变队列元素的先后顺序 且 不要求最高同学左右人数必须相等
数据范围: 1 ≤ n ≤ 3000 1 \le n \le 3000 1≤n≤3000
HJ103 Redraiment的走法
Redraiment是走梅花桩的高手。Redraiment可以选择任意一个起点,从前到后,但只能从低处往高处的桩子走。他希望走的步数最多,你能替Redraiment研究他最多走的步数吗?
数据范围:每组数据长度满足 1 ≤ n ≤ 200 1 \le n \le 200 1≤n≤200 , 数据大小满足 1 ≤ v a l ≤ 350 1 \le val \le 350 1≤val≤350
代码和解释
思路
这两道题本质上都是求一串数字最长递增子序列的长度。
HJ24这一题的合唱队队形要求可知,从左往右看,最高的人左边身高是递增的,右边则是递减的。因此等价于求队列里的一个位置,这个位置左边的最长递增子序列,和这个位置右边的最长递减子序列,这两个序列长度之和最大的情况。因为这时候需要最少的人出列就可以形成合唱队形。
而最长递减子序列逆序便是最长递增子序列,故只需要写出判断最长递增子序列的代码,两次逆序便可以求出一个数列的最长递减子序列。
代码实现
- 参数H是记录输入数列的列表,N是数列的长度。放在HJ24的情形下,H是记录同学们身高的列表,N是同学的总数。
- 函数
bisect_left(L,a)
返回a在L中的位置。若不存在,则返回插入a能使L有序的位置。 - 一位数组dp[i]记录H[0]~H[I]的最大递增子序列的长度,列表arr记录当前递增子序列。遍历数列H,如果当前数H[i]大于arr的最后一位,说明H[i]加入到arr末尾,不改变它的递增性。
否则,找到H[i]在数组arr中的位置,可求dp[i]
import bisect
def f(H,N):
dp=[1]*N
arr=[H[0]]
for i in range(1,N):
if H[i]>arr[-1]:
arr.append(H[-1])
dp[i]=len(arr)
else:
pos=bisect.bisect_left(arr,H[i])
arr[pos]=H[i]
dp[i]=pos+1
return dp