好吧,让我们看看 19 美国大学生竞赛 A 题的翻译。
**翻译:**
给定一个长度为 n 的序列 a,序列中的每个元素都是一个整数(1 ≤ a[i] ≤ k),求满足下列条件的子序列 b 的数量:
1. b 是 a 的一个非空子序列。
2. 对于任何 i(1 ≤ i < |b|),b[i] < b[i+1]。
3. b 中每个元素至少出现过一次。
问题解读
通俗来讲,就是给定一个数字序列,让你从中挑一个子序列,这个子序列中的数字必须从小到大排列,而且每个数字都得在子序列中出现至少一次。问你一共有多少种挑法。
**示例:**
* **输入:** [1, 2, 3]
* **输出:** 6
因为可能的子序列有:
* [1]
* [1, 2]
* [1, 2, 3]
* [1, 3]
* [2]
* [2, 3]
解题思路
这道题乍一看有点绕,其实不难。我们可以用动态规划来解决。
设 dp[i][j] 表示以第 i 个元素结尾的,包含第 j 个不同元素的子序列的数量。那么对于每个 i,我们有以下转移方程:
* **如果 a[i] > j:** dp[i][j] = dp[i-1][j](因为子序列中不能有比 j 更大的元素)
* **如果 a[i] = j:** dp[i][j] = dp[i-1][j-1] + dp[i-1][j](要么不选 a[i],要么选 a[i])
* **如果 a[i] < j:** dp[i][j] = dp[i-1][j-1] + dp[i-1][j] + dp[i-1][j+1](不选 a[i],选 a[i],选一个比 a[i] 大的元素)
代码实现
Python 代码如下:
```python
def count_subsequences(arr, k):
n = len(arr)
dp = [[0] * (k + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, k + 1):
if arr[i - 1] > j:
dp[i][j] = dp[i - 1][j]
elif arr[i - 1] == j:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
else:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j] + dp[i - 1][j + 1]
return dp[n][k]
```
这道题考查了动态规划的基本思想。只要我们把问题分解成子问题,再用转移方程把子问题的解联系起来,就可以得到问题的整体解法。