📌  相关文章
📜  最小和的K个长度子序列的数量(1)

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

最小和的K个长度子序列的数量

给定一个由正整数构成的长度为n的序列,对于一个指定的k,设计算法求解该序列中长度为k的子序列中各元素之和最小的k个子序列的个数。

思路

首先,我们可以想到将长度为k的子序列都枚举一遍,统计它们的和,然后选出前k小的和所对应的子序列。但是,这样做的时间复杂度是$O(n^k)$,在n和k较大时会很慢。所以,我们需要寻找更优秀的算法。

根据题意,我们要求的是长度为k的子序列中各元素之和最小的k个子序列。我们可以根据动态规划的思路来处理,对于当前正在考虑的位置i,维护一个二维数组$dp[i][j]$表示包含i位置的长度为j的子序列中,各元素之和的最小值。则状态转移方程为:

dp[i][j] = min(dp[i-1][j-1], dp[i-2][j-1], ..., dp[j-1][j-1]) + a[i]

其中,$a[i]$表示第i个位置的元素值。

代码片段如下所示:

n = len(a)
dp = [[float('inf')] * (k+1) for _ in range(n)]

for i in range(k, n):
    dp[i][1] = sum(a[i-k+1:i+1])
    for j in range(2, min(i-k+2, k+1)):
        for t in range(j-1, i-k+j):
            dp[i][j] = min(dp[i][j], dp[t-1][j-1] + sum(a[t:i+1]))

上述代码使用了三重循环,时间复杂度为$O(nk^2)$。对于每个位置i和长度j,要枚举t来更新dp[i][j],其中$t \in [j-1, i-k+j]$。我们可以使用单调队列来进行优化,使时间复杂度降为$O(nk)$。

代码片段如下所示:

from collections import deque

n = len(a)
dp = [[float('inf')] * (k+1) for _ in range(n)]
q = deque()

for i in range(k, n):
    while q and dp[q[-1]][q[0]+1] >= dp[i-k][q[0]+1]:
        q.pop()
    q.append(i-k)
    if i-q[0]+1 > k:
        q.popleft()

    dp[i][1] = sum(a[i-k+1:i+1])
    for j in range(2, min(i-k+2, k+1)):
        while q and dp[q[-1]][j-1] >= dp[i][j-1]:
            q.pop()
        dp[i][j] = dp[q[-1]][j-1] + sum(a[q[-1]+1:i+1])
返回格式
# 最小和的K个长度子序列的数量

## 思路

- 动态规划

## 代码

- 时间复杂度: $O(nk^2)$

```python
n = len(a)
dp = [[float('inf')] * (k+1) for _ in range(n)]

for i in range(k, n):
    dp[i][1] = sum(a[i-k+1:i+1])
    for j in range(2, min(i-k+2, k+1)):
        for t in range(j-1, i-k+j):
            dp[i][j] = min(dp[i][j], dp[t-1][j-1] + sum(a[t:i+1]))
```

- 时间复杂度: $O(nk)$

```python
from collections import deque

n = len(a)
dp = [[float('inf')] * (k+1) for _ in range(n)]
q = deque()

for i in range(k, n):
    while q and dp[q[-1]][q[0]+1] >= dp[i-k][q[0]+1]:
        q.pop()
    q.append(i-k)
    if i-q[0]+1 > k:
        q.popleft()

    dp[i][1] = sum(a[i-k+1:i+1])
    for j in range(2, min(i-k+2, k+1)):
        while q and dp[q[-1]][j-1] >= dp[i][j-1]:
            q.pop()
        dp[i][j] = dp[q[-1]][j-1] + sum(a[q[-1]+1:i+1])
```

其中,a为输入序列,k为指定的长度。时间复杂度为$O(nk^2)$的代码需要使用三重循环,时间复杂度为$O(nk)$的代码使用了单调队列进行优化。