动态规划和分治法解合唱队形问题

18 篇文章 4 订阅
18 篇文章 0 订阅

问题:

合唱队形安排问题(使用动态规划和分治算法求解)
问题描述:N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,其余人位置不变,使得剩下的K位同学排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<…Ti+1>…>TK(1<=i<=K)。已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

解:

  • 动态规划方法:

先从左到右找出当前数字的最长递增序列,并计算出长度,然后同理找出从右到左的最长递增序列,最后再将得出的两个序列按照对应位置相加然后减一得出的列表即为当其为合唱队中间人时,合唱队的长度。之所以减一,是因为相加是,加了两遍自己。

# -*- coding: utf-8 -*-
"""
Created on Tue Oct 25 10:24:45 2022

@author: Dell
"""

# 每个人的左边出现的最多人(输出左_最长递增子序列)
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:
        N=8 
        #tall_li_str=input().split()
        tall_li_int=[165, 182, 178, 149, 193, 179, 174, 158] 
        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,'位同学')#题中问的是最少去几人,也就是总人数减去合唱队最多人数
        # 另外加和时自己算了两遍,还得再减去一遍
        break

运行结果:
在这里插入图片描述

  • 分治法
import bisect
def max_order(lists):
    list_num = [] #用于排序,
    list_max = [] #每个元素右边的最大数(包括元素自己)
    for i in lists:
        local = bisect.bisect_left(list_num, i)
        if local == len(list_num):
            list_num.append(i)
            list_max.append(local+1)
        else:
            list_num[local] = i
            list_max.append(local+1)
    return list_max

while True:
     N = 8
     tall_li_int = [165, 182, 178, 149, 193, 179, 174, 158]
     left_li = max_order(tall_li_int)
     right_li = max_order(tall_li_int[::-1])[::-1]
     sum_s = []
     for i in range(len(left_li)):
         # left_s[i]+right_s[i]-1表示此人是中间位置的人时,合唱队的人数
         sum_s.append(left_li[i]+right_li[i]-1)
     print('最少要出列',str(N-max(sum_s)),'位同学')
     break


运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南木Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值