先升后降,LIS,LDS包含路径的寻找

先升后降
从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的,连续出现的数值不应该有相等的情况。
输入:1 2 4 7 11 10 9 15 3 5 8 6 输出:1 2 4 7 11 10 9 8 6
这道题用的是最长上升子序列(LIS)和最长下降子序列(LDS)

def LIS(a):  # a是待处理的数组
    n = len(a)
    f = [1] * n # 初始化f
    for i in range(0, n):
        for j in range(0, i):
            if a[j] < a[i]:
                f[i] = max(f[i], f[j]+1) # 最重要的一步
    return f  # 返回的是处理后的序列

输入:1 2 4 7 2 11
输出:1 2 3 4 2 5
对于1,前面没有比它小的元素,f[0]=1
对于2,前面1比2小,f[1]=max(f[i], f[j]+1),则f[1]=2
对于4,前面比4小的元素1,2,则f[2]=3
对于7,同理,f[3]=4
对于2,前面2小的元素,只有1,f[4]=2
...
至此,原数组a 更新为 f
最长递减子序列,同理,唯一改变的是a[j] > a[i],此元素前面有没有比它的元素。
时间复杂度O(n2)

def LDS(a):
    n = len(a)
    f = [1] * n
    for i in range(0, n):
        for j in range(0, i):
            if a[j] > a[i]:
                f[i] = max(f[i], f[j]+1)
    return f

更新后的f,找到了,那么怎样去原数组中,找到这样的序列呢?
百度说,这是寻找路径,想了好久,才想明白,以递增为例讲述
例如f为1 2 2 3 3 4 5 2 1
观察f,可以知道,最长递增子序列的长度是5(f中元素的最大值)
在f中,从5开始,向前遍历,找到5,对应 a中的元素,将元素放人Rlis[]中(f和a下标是一一对应的,通过f中的下标,可以找到a中的元素)
之后依次放入4,3,2,1下标对应a中的元素
5的下标,可以通过f.index(max(f))(这是寻找最值下标的方式)
python中,枚举列表时,索引仍然是从前先后的,这里用了一个标志,flag,最初flag=f.inf.index(max(f)),从后向前循环f,找到5,4,3,2,1对应a中的元素,存入数组,倒序输出。
1、flag,用于索引,下标,找到a中对应的元素
2、K,5,4,3,21,找到5对应的元素后,K-1,接着找4,与f中的元素一一对应
1,2是我认为比较重要的标记
如果,f数组是这样的1,2,2,1,3,4,5,有两个1,2,选哪一个呢?
例如,找到3后,K-1,K=2,最先遇见的2,就是一定符合要求的2,当然 面前的一个2也可能符合要求,这里我们要找一定可以符合要求的
x1 x2 x3 在数组中例如都是2
那么x3 为什么?如果x3>x2,x3就会是3,不是2了

def path(f, a): 
    r = []  # path 用于存储路径
    K = max(f)   # f 中的最大值
    N = f.index(max(f))  # 找到f最大值 的下标
    flag = N   # 从最大下标开始,遍历f,小于最大下标的,都可以直接跳过
    for i in f[N::-1]:  # 从后向前遍历,index 仍然是0,1,2,3;所以用了一个flag
        # print(i)
        if i == K:
            r.append(a[flag])   # 原数组
            K = K - 1
        flag = flag - 1
    # print('最长子序列', r[::-1])
    return r[::-1]

先升后降的代码

# 第七题 先升后降
# import random
# random.shuffle(arr)
# print('原数列:', arr)


arr = list(map(int, input().split()))


def LIS(a):
    n = len(a)
    f = [1] * n
    for i in range(0, n):
        for j in range(0, i):
            if a[j] < a[i]:
                f[i] = max(f[i], f[j]+1)
    return f

# print('最长LIS:', max(f))
# print('计算后的',f)


def LDS(a):
    n = len(a)
    f = [1] * n
    for i in range(0, n):
        for j in range(0, i):
            if a[j] > a[i]:
                f[i] = max(f[i], f[j]+1)
    return f


# f = LDS(arr)
# print('最长LDS:', max(f))
# print('计算后的',f)


def path(f, a):
    r = []  # path 用于存储路径
    K = max(f)   # f 中的最大值
    N = f.index(max(f))  # 找到f最大值 的下标
    flag = N   # 从最大下标开始,遍历f,小于最大下标的,都可以直接跳过
    for i in f[N::-1]:  # 从后向前遍历,index 仍然是0,1,2,3;所以用了一个flag
        # print(i)
        if i == K:
            r.append(a[flag])   # 原数组
            K = K - 1
        flag = flag - 1
    # print('最长子序列', r[::-1])
    return r[::-1]

#
# print(path(f))


MAX = 1  # 计算先升后降
Flag = 1  # 从哪儿断开比较合适 存储索引
list1 = []
list2 = []
for index, item in enumerate(arr):
    list1 = LIS(arr[:index+1])
    list2 = LDS(arr[index:])
    tmp = max(list1)+max(list2)-1
    if tmp > MAX:
        MAX = tmp
        Flag = index

# 分成两个数组,分别计算
list1 = arr[:Flag+1]
list2 = arr[Flag:]


left = path(LIS(list1), list1)
right = path(LDS(list2), list2)
right.pop(0)  # 去除重复的
left.extend(right) # 合并在一起
for index, item in enumerate(left):
    left[index] = str(item)
print(*left)

新手入门,暴力解题,不完美
请大神轻喷

你可能感兴趣的:(先升后降,LIS,LDS包含路径的寻找)