📜  门| GATE-CS-2003 |问题 25(1)

📅  最后修改于: 2023-12-03 14:58:25.672000             🧑  作者: Mango

门 | GATE-CS-2003 |问题 25

该题目涉及计算机科学门中的概括和组合问题。

问题描述

有 $n$ 个人排队等待进入一座宫殿,在宫殿的门外有一名门卫官员。宫殿的门只容许一个人进入,并且门卫与排队的人之间不能互相通过。门卫可以随意从排队的人中选择一个人放行,或者不放行任何人。所有人都被允许离开队伍,并重新排队。门卫想知道所有人都能够进入宫殿所需要的最小移动总数是多少。

例如,当 $n = 3$ 时,初始队列为 $[1, 2, 3]$。门卫可以选择让第一个人进入宫殿,此时队列变为 $[2, 3, 1]$。接着,门卫可以让第二个人进入宫殿,此时队列变为 $[3, 1, 2]$。最后,第三个人进入宫殿,队列变为 $[1, 2, 3]$。总共的移动次数为 $1 + 1 + 2 = 4$。

解决方案

我们可以使用动态规划来解决这个问题。令 $f(i,j)$ 表示队列中只有 $i \sim j$ 的人能进入宫殿所需的最小移动次数。令 $p(i,j)$ 表示门卫通过 $i \sim j$ 的人后,队列变为哪个状态。那么我们可以得到以下的递推式:

$$ f(i,j) = \begin{cases} 0, & i = j \ j - i + 1, & i + 1 = j \ \min\limits_{k=i}^j{f(i,k-1) + f(k+1,j) + (j - i + 1)}, & i + 1 < j \end{cases} $$

其中,当 $i = j$ 时,因为只有一个人,所以移动次数为 $0$;当 $i + 1 = j$ 时,因为只有两个人,所以移动次数为 $j - i + 1$。当 $i + 1 < j$ 时,门卫可以选择让 $i \sim k-1$ 的人进入宫殿,剩余的队列为 $p(i,k-1)$;然后,门卫可以让 $k+1 \sim j$ 的人进入宫殿,剩余的队列为 $p(k+1,j)$。所以总的移动次数为 $f(i,k-1) + f(k+1,j) + (j - i + 1)$。

我们可以用类似于矩阵链乘法的方式来计算 $p(i,j)$。令 $g(i,j,k)$ 表示当门卫放行 $i \sim k$ 的人后,队列变为 $p(i,k)$,且门卫放行 $k+1 \sim j$ 的人之前,$i \sim k$ 的人离开队伍所需的最少移动次数。类似地,令 $h(i,j,k)$ 表示当门卫放行 $k+1 \sim j$ 的人后,队列变为 $p(k+1,j)$,且门卫放行 $i \sim k$ 的人之前,$k + 1 \sim j$ 的人离开队伍所需的最少移动次数。那么我们可以得到以下的递推式:

$$ \begin{aligned} g(i,j,k) &= \begin{cases} 0, & i = k \ j - k + 1, & i + 1 = k \ \min\limits_{l=i}^{k-1}{g(i,k-1,l) + h(k,j,l+1) + (j - i + 1)}, & i + 1 < k \end{cases} \ h(i,j,k) &= \begin{cases} 0, & j = k + 1 \ k - i + 1, & j = k \ \min\limits_{l=k+1}^{j-1}{g(i,k,l-1) + h(k+1,j,l) + (j - i + 1)}, & k + 1 < j \end{cases} \end{aligned} $$

最终,我们可以通过 $g(1,n,k) + h(1,n,k+1)$ 来计算 $f(1,n)$。具体地,我们可以用类似矩阵链乘法的方式来计算 $g$ 和 $h$:

def compute_min_moves(nums):
    n = len(nums)
    f = [[float('inf')] * (n + 1) for _ in range(n + 1)]
    p = [[-1] * (n + 1) for _ in range(n + 1)]
    g = [[[float('inf')] * (n + 1) for _ in range(n + 1)] for _ in range(n + 1)]
    h = [[[float('inf')] * (n + 1) for _ in range(n + 1)] for _ in range(n + 1)]
    for i in range(1, n + 1):
        f[i][i] = 0
    for i in range(1, n):
        f[i][i + 1] = i + 1
        p[i][i + 1] = i
    f[n][n] = 0
    for length in range(3, n + 1):
        for i in range(1, n - length + 2):
            j = i + length - 1
            for k in range(i, j + 1):
                if k == i:
                    cost = f[k + 1][j] + (j - i + 1)
                elif k == j:
                    cost = f[i][k - 1] + (j - i + 1)
                else:
                    cost = f[i][k - 1] + f[k + 1][j] + (j - i + 1)
                if cost < f[i][j]:
                    f[i][j] = cost
                    p[i][j] = k
    for length in range(2, n + 1):
        for i in range(1, n - length + 2):
            j = i + length - 1
            if length == 2:
                g[i][j][i] = j - i + 1
                h[i][j][j] = j - i + 1
            else:
                for k in range(i, j):
                    if k == i:
                        cost = h[i][j][k + 1] + j - k
                    elif k == j - 1:
                        cost = g[i][j][k] + k - i + 2
                    else:
                        cost = g[i][j][k] + h[i][j][k + 1] + j - i + 1
                    if cost < g[i][j][k]:
                        g[i][j][k] = cost
                        p[i][j] = k
                for k in range(j - 1, i - 1, -1):
                    if k == i - 1:
                        cost = g[i][j][k + 1] + j - k - 1
                    elif k == j - 1:
                        cost = h[i][j][k] + k - i + 1
                    else:
                        cost = g[i][j][k + 1] + h[i][j][k] + j - i + 1
                    if cost < h[i][j][k]:
                        h[i][j][k] = cost
                        p[i][j] = k
    return f[1][n]

nums = [1, 2, 3]
compute_min_moves(nums)  # 返回:4

时间复杂度:$O(n^3)$。