📜  门| GATE-CS-2016(套装1)|第 61 题(1)

📅  最后修改于: 2023-12-03 15:12:43.291000             🧑  作者: Mango

题目概述

本题目为GATE-CS-2016(套装1)题库中的第61道题,考察程序员对于动态规划的理解和应用。本题目难度较高,需要有一定的算法和编程基础,以下是具体要求:

题目要求

对于$N$个数$(a_1,a_2,\cdots,a_N)$,将它们排列后可以得到很多不同的排列情况,并且每种排列情况有一定的贡献值。现在要求找到一个排列,使得这个排列的贡献值最大。

每种排列的贡献值计算方式如下:

  • 对于第$i$个数$a_i$,如果它在排列中恰好比$a_j$小,则贡献值为$i-j$。
  • 如果它在排列中恰好比$a_j$大,则贡献值为$j-i$。
  • 如果它与$a_j$相等,则贡献值为$0$。

例如,对于长度为$N=4$,序列为$(2,3,1,4)$的排列,根据计算方式,它的贡献值应该是:

  • 对于$a_1=2$,$2$在排列中比$1$大, 比$3$小, 比$4$小, 所以它的贡献值为$1+2+2=5$。
  • 对于$a_2=3$,$3$在排列中比$2$大, 比$1$小, 比$4$小, 所以它的贡献值为$1+0+1=2$。
  • 对于$a_3=1$,$1$在排列中比$2$小, 比$3$小, 比$4$大, 所以它的贡献值为$0+1+1=2$。
  • 对于$a_4=4$,$4$在排列中比$2$大, 比$1$大, 比$3$大, 所以它的贡献值为$1+2+2=5$。

因此,这个排列的总贡献值为$5+2+2+5=14$。

请根据上述要求,撰写一段动态规划算法,计算一个指定序列$(a_1,a_2,\cdots,a_N)$的最大贡献值。

解题思路

令$C(i,j)$表示$a_i$和$a_j$在排列中这两个元素之间的“逆序对数”,也即$a_i$在排列中比$a_j$小的位置数目。那么,题目中给出的贡献值则可以改写为:

  • 如果$a_i$在排列中恰好比$a_j$小,则贡献值为$i-j+C(i,j)$。

注意到,当$i<j$时,$C(i,j)=C(i,j-1)+C(j,j-1)-C(i,j)(a_i<a_j)$,其中$(a_i<a_j)$的值为真或假,$a_i<a_j$时为真,否则为假。

于是,可以使用动态规划的思路来计算。令$m_i$表示所有$C(i,j)$的和,则$m_i$可以使用$m_{i-1}$来计算:$m_i=m_{i-1}+\sum_{j=1}^{i-1}j<i-1$。

代码实现

以下是一份可能的Python实现,时间复杂度为$O(N^2)$:

def max_contribution(a):
    n = len(a)
    c = [[0] * n for _ in range(n)]
    for i in range(n):
        for j in range(i):
            c[i][j] = c[i-1][j] + c[i-1][j-1] - c[i-1][j] + (a[i] < a[j])
    m = [0] * n
    for i in range(1, n):
        m[i] = m[i-1] + sum(m[j] - m[i-1] + c[i-1][j] for j in range(i))
    return sum((i-j+c[i-1][j]) for i in range(n) for j in range(i))

以上实现代码同时也作为代码片段供用户参考,开发者可以根据需求自由调整。