算法题总结-最长递增子序列
原题
https://www.nowcoder.com/practice/6d9d69e3898f45169a441632b325c7b4?tpId=37&tqId=21247&rp=1&ru=/exam/oj/ta&qru=/exam/oj/ta&sourceUrl=%2Fexam%2Foj%2Fta%3Fdifficulty%3D3%26page%3D1%26pageSize%3D50%26search%3D%26tpId%3D37%26type%3D37&difficulty=3&judgeStatus=undefined&tags=&title=
N 位同学站成一排,音乐老师要请最少的同学出列,使得剩下的 K 位同学排成合唱队形。
设KK位同学从左到右依次编号为 1,2…,K ,他们的身高分别为T_1,T_2,…,T_K,若存在i(1≤i≤K) 使得T_1<T_2<......<T_{i-1}<T_i且 T_i>T_{i+1}>......>T_K,则称这KK名同学排成了合唱队形。
通俗来说,能找到一个同学,他的两边的同学身高都依次严格降低的队形就是合唱队形。
例子:
123 124 125 123 121 是一个合唱队形
123 123 124 122不是合唱队形,因为前两名同学身高相等,不符合要求
123 122 121 122不是合唱队形,因为找不到一个同学,他的两侧同学身高递减。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
注意:不允许改变队列元素的先后顺序 且 不要求最高同学左右人数必须相等
数据范围: 1≤n≤3000
输入描述:
用例两行数据,第一行是同学的总数 N ,第二行是 N 位同学的身高,以空格隔开
输出描述:
最少需要几位同学出列
输入示例:
8
186 186 150 200 160 130 197 200
输出示例:
4
解析:
所谓的合唱队形,可以划分为左侧的递增序列与右侧的递减序列(反向也是递增序列),因此,该问题可转化为最长递增子序列的问题
核心转移方程:
dp[i] = max(dp[j]+1,dp[i])
dp[i]表示位置i上的同学,前面有dp[i]个递增元素。当data[j]小于data[i]时,dp[i]则是dp[j]的最优解+1
注意事项:
1、正序的递增与逆序的递增,在初始化的时候自身被计算了两次,因此,求解最后结果时,需要减掉冗余
最长递增序列求解:
def calculate(data:list)->list:
# 核心转移方程: dp[i] = max(dp[j]+1,dp[i])
dp = [1 for i in range(len(data))]
for i in range(len(data)):
for j in range(i):
if data[j] < data[i]:
dp[i] = max(dp[j] + 1, dp[i])
pass
pass
return dp
a = calculate(data)
b = calculate(data[::-1])[::-1]
c = [a[i]+b[i]-1 for i in range(len(a))]
print(n-max(c))