其他分享
首页 > 其他分享> > 牛客华为机试HJ24

牛客华为机试HJ24

作者:互联网

原题传送门

1. 题目描述

2. Solution

1、思路分析
此题是最长递增子序列的变体,基本思路是对原序列从左到右和从右到左分别求出到每个元素的最长递增子序列的长度。例如,原序列为长度为n的序列[8,20,12,15,10,9],从左至右的到序列里每个元素的最长递增子序列为l1=[1,2,2,3,2,2],从右至左为l2=[1,4,3,3,2,1],l1+l2=[2,6,5,6,4,3]。那么合唱队最长队伍是L = max(l1+l2)-1,减1是因为计算l1和l2时重复计算了一次元素本身。因此最少出列人数为原序列长度N-L。
此题关键在于求出l1,l2。可由动态规划求出。用dp[i]表示从左至右到原序列第i个元素的最长递增子序列的长度,从第i个元素往回遍历更新dp[i]的值。由于每个元素都需要往回遍历一次,时间复杂度是o(n^2)。往回遍历如何更新dp[i]的值在其他题解已有很好的介绍,这里主要写用二分法代替往回遍历的过程,时间复杂度是o(nlogn)。
二分法的过程为:首先创建数组arr=[ele_1],ele_1是原序列第一个元素,然后从第二个元素开始从左至右遍历原序列
1、如果ele_i > max(arr),将ele_i加到arr最后;
2、如果ele_i <= max(arr),用二分法找到arr中第一个比ele_i大(或相等)的元素并用ele_i替换。

2、代码实现

import bisect #引入二分法

def inc_max(l): #定义一个函数,寻找最长的子序列
    arr = [l[0]] #定义列表,将传入函数的列表第一个元素放入当前元素
    dp = [1]*len(l) #定义一个列表,默认子序列有当前元素1,长度是传入函数的列表长度
    for i in range(1,len(l)): #从第二个元素开始查找
        if l[i] > arr[-1]: #如果元素大于arr列表的最后一个元素,就把它插入列表末尾
            arr.append(l[i])
            dp[i] = len(arr)# 获取这个元素子序列的长度
        else: # 否则,利用二分法找到比元素大的元素的位置,用新的元素替代比它大的那个元素的值,这样就能制造出一个顺序排列的子序列
            pos = bisect.bisect_left(arr, l[i])
            arr[pos] = l[i]
            dp[i] =pos+1 # 获取这个元素子序列的长度
    return dp

while True:
    try:
        n = int(input())
        s = list(map(int,input().split()))
        left_s = inc_max(s) #向左遍历查找子序列
        right_s = inc_max(s[::-1])[::-1] #向右遍历查找子序列
        res = [left_s[i]+right_s[i]-1 for i in range(len(s))] #因为左右都包含原元素,所以需要减1 ;得到各元素能得到的子序列的最大长度
        print(n-max(res)) # 源列表长度-可以生成的最长子序列长度  得到需要剔除的最小人数
    except:
        break

标签:arr,遍历,元素,牛客,长度,序列,机试,HJ24,dp
来源: https://www.cnblogs.com/junstat/p/16163638.html