【牛客】美团2024届秋招笔试第一场编程真题 - 5. 小美的排列构造【未完全解出】

小美定义一个数组a的权值计算如下:

首先将a的每一对相邻两项求和,得到一个b数组。那么b数组的最大值减最小值即为a数组的权值。

例如,若a*=[2,1,3],那么b=[3,4],b数组的极差是1。因此a*数组的权值为1。

现在小美希望你能构造一个长度为n的排列,满足权值尽可能小。你能帮帮她吗?

排列是指一个长度为n的数组,其中 1 到n每个元素恰好出现一次。

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M
输入描述:
一个正整数,代表排列的长度。
2 ≤ n ≤ 200000
输出描述:
一个合法的排列。如果有多解输出任意即可。
示例1

输入例子:

3

输出例子:

2 1 3

例子说明:

这个数组的权值为 1。输出[2,3,1]等排列也是合法的。
思路:
  1. 排列组合求权值,n=3时有123、132、213、231、312、321共3!种排列法,其中要求权值最小为1的。

  2. 此时看列表123:1+2=3,2+3=5;

    ​ 再看列表213:2+1=3,1+3=4;此时还看不出什么,于是构造长度为4的列表;

  3. list[1, 2, 3, 4]:只需满足最大-最小=极小,则(4+1)-(3+1)=1,原序列变为list[2, 3, 1, 4]。再构造一个len5列表观察一下;

  4. list[1, 2, 3, 4, 5]:变为list[2, 3, 4, 1, 5],发现权值为2,是否还存在更小权值呢?构造list[2, 3, 1, 4, 5]权值为5;

  5. list[1, 2, 3, 4, 5, 6] -> weight = 10

    list[2, 3, 4, 5, 1, 6] -> weight = 3

    list[2, 3, 4, 1, 5, 6] -> weight = 6

  6. 在此猜测,只要挪动1的位置,就可以使最小值极大,最大值极小,以此求出极小值。

  7. 以n=5的列表举例:

    若list_a[1, 2, 3, 4, 5] -> list_b1[3, 5, 7, 9] -> 9 - 3 = 6

    交换list_a[i], list_a[i+1] = list_a[i+1], list_a[i]

    若list_a[2, 1, 3, 4, 5] -> list_b2[3, 4, 7, 9] -> 9 - 3 = 6

    ……(此时运行第一个程序发现题解错误)

    # 改变1的位置
    def change_loc(_list):
        _list.pop(0)
        _list.insert(-1,1)
        return _list
    
    # 输入长度
    n = int(input())
    
    # 构建长度为n的列表a
    list_a = list(range(1,n+1))
    
    # 调用
    new_list_a = change_loc(list_a)
    
    for i in range(len(new_list_a)):
        print(new_list_a[i],end=" ")
    
    

    预期输出

    1 37 2 36 3 35 4 34 5 33 6 32 7 31 8 30 9 29 10 28 11 27 12 26 13 25 14 24 15 23 16 22 17 21 18 20 19
    

    实际输出

    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 1 37 
    

    若list_a[1, 5, 2, 4, 3] -> list_b2[6, 7, 6, 7] -> 7 - 6 = 1

    我靠,还能这么搞

    此时犹如醍醐灌顶,茅塞顿开,但仔细一想暴力枚举出weight肯定是不行的,一共有n!种排列方式肯定会超时间,那么看实际输出:

    此时只要保持 (a[ i ] + a[ i + 1 ]) - (a[ i + 1 ] + a[ i + 2 ]) = 1 就可以了

    实际上也可以输出

    19 18 20 17 21 16 22 15 23 14 24 13 25 12 26 11 27 10 28 9 29 8 30 7 31 6 32 5 33 4 34 3 35 2 36 1 37 
    

    那么就可以根据结果来设计思路:

    1. 二分列表到位置n/2,获取list_a_1和list_a_2

      n = 5
      list_a = [1, 2, 3, 4, 5]
      list_a_1 = list_a[:int(n/2)]	# 列表取一半位置
      list_a_2 = list(set(list_a)-set(list_a_1))	# 取列表补集
      
      out:
      [1, 2]
      [3, 4, 5]
      
    2. 倒序list_a_2

    3. 将list_a_2依次插入list_a_1(或者反过来插)

      list_3 = []
      for i in range(max(len(list_a_1),len(list_a_2_reverse))):
          if len(list_a_1) - 1 >= i:
              list_3.append(list_a_1[i])
          if len(list_a_2_reverse) - 1 >= i:
              list_3.append(list_a_2_reverse[i])
      return list_3
      
题解
def resetlist(_list):
    list_a_1 = _list[:int(n / 2)]
    list_a_2 = list(set(_list) - set(list_a_1))
    list_a_2_reverse = list(reversed(list_a_2))
    list_3 = []
    for i in range(max(len(list_a_1),len(list_a_2_reverse))):
        if len(list_a_1) - 1 >= i:
            list_3.append(list_a_1[i])
        if len(list_a_2_reverse) - 1 >= i:
            list_3.append(list_a_2_reverse[i])
    return list_3

# 输入长度
n = int(input())

# 构建长度为n的列表a
list_a = list(range(1,n+1))

# 调用
_list = resetlist(list_a)
for i in _list:
    print(i, end=' ')

26/30 组用例通过

运行时间654ms

占用内存17636KB

为什么有4组用例不能通过啊,求高人指点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值