zoukankan      html  css  js  c++  java
  • 动态规划——合唱队

    一、问题描述

    计算最少出列多少位同学,使得剩下的同学排成合唱队形

    说明:

    N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
    合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,   则他们的身高满足存在i(1<=i<=K)使得T1<T2<......<Ti-1<Ti>Ti+1>......>TK。

    你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
     
    注意不允许改变队列元素的先后顺序
    请注意处理多组输入输出!
     

    输入描述:

    整数N

    输出描述:

    最少需要几位同学出列

    输入示例

    8
    186 186 150 200 160 130 197 200

    输出示例

    4

    二、思路

    先从左到右找出当前数字的最长递增序列,并计算出长度,然后同理找出从右到左的最长递增序列。

    拿题目示例来说,对于列表 [186, 186, 150, 200, 160, 130, 197, 200] 而言,先看每个人的左边可能出现最多的人。首先如果第一个数186在中间,左边没有数,就自己一个人,所以是1;第二个数186因为左边那个人跟他一边高,没有比他矮的了,所以也是1;第三个数150,左边的人都比他高,他如果是中间的话左边也他自己一个人,所以还是1;第四个数200,因为不能换位置,所以只能留186或者150,加上自己,就是2...最后再以197为例,左边保留150,160是左边人最多的情况,再加上自己,就是3。所以每个人的最长递增子序列长度为 [ (186)1 1 1 2 2 1 3 4(200) ].

    同理从右往左搜索,200在最右面,所以自己一个人,是1;197最右面没有比他矮的,自己,是1...160左边一个比他矮的,所以算上自己是2,以此类推。所以每个人右边人做多的情况(加上自己)就是[(186)3 3 2 3 2 1 1 1(200)].

    最后再将得出的两个序列按照对应位置相加然后减一得出的列表即为当其为合唱队中间人时,合唱队的长度。之所以减一,是因为相加是,加了两遍自己。

    所以我的代码如下:

    # 每个人的左边出现的最多人(输出左_最长递增子序列)
    def left_max(l):#l为站成一排的同学序列
        ans=[1]*len(l)
        for i in range(len(l)):# 每个人的游标(从前往后)
            for j in range(i):# 这个人前面每个人的游标
                if l[j]<l[i] and ans[j]+1>ans[i]:
                    ans[i]=ans[j]+1
        return ans # 1 1 1 2 2 1 3 4
     
    # 每个人的右边出现的最多人(输出右_最长递增子序列)
    def right_max(l):
        ans=[1]*len(l)
        for i in range(len(l)-1,-1,-1):# 每个人的游标(从后往前)
            for j in range(i+1,len(l)):# 这个人后面每个人的游标
                if l[j]<l[i] and ans[j]+1>ans[i]:
                    ans[i]=ans[j]+1
        return ans # 3 3 2 3 2 1 1 1
     
    while True:
        try:
            N=int(input())#8
            tall_li_str=input().split()
            tall_li_int=[int(v) for v in tall_li_str]#[186,186,150,200,160,130,197,200]
            left_li=left_max(tall_li_int)
            right_li=right_max(tall_li_int)
            sum_li=[]#left_li和right_li加和,可以得到每个人如果是中间那个人的话,合唱队最长是多少(自己算两遍)
            for i in range(len(left_li)):
                sum_li.append(left_li[i]+right_li[i])
            print(N-max(sum_li)+1)#题中问的是最少去几人,也就是总人数减去合唱队最多人数
            # 另外加和时自己算了两遍,还得再减去一遍
        except:
            break
  • 相关阅读:
    字符串初始化、查找字符+获取字符
    冒泡排序
    JAVA中值类型和引用类型的不同?
    二维数组初始化,属性,遍历,输出各元素总和。
    数组定义属性遍历循环,输出最大数
    for穷举,叠代练习
    HTML--Boby部分之<a>标签
    HTML--Boby内标签之多行文本及下拉框
    HTML--Boby部分Input之重置
    HTML--Boby部分Input之上传文件
  • 原文地址:https://www.cnblogs.com/sunny0824/p/13510263.html
Copyright © 2011-2022 走看看