📜  门| GATE-CS-2000 |第 39 题(1)

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

门 | GATE-CS-2000 |第 39 题

题目描述

枚举一个 $n$ 位的二进制数,从其中选取若干个位置,使得选出来的位置上的数字之和是 $k$。求方案数。

示例

给定 $n=5, k=2$,方案数为 $10$,具体方案如下:

00100
00010
00001
10000
01000
00101
00011
10001
01001
00110
解题思路

这道题可以用递归或者动态规划来解决。

递归

我们定义一个递归函数 count(n, k) 表示选取一个 $n$ 位的二进制数,选取若干个位置后其数字之和为 $k$ 的方案数。具体实现过程如下:

  • 如果 $k=0$,表示数字之和为 $0$,只有一种方案:不选。
  • 如果 $n<k$,表示位数不足以选出 $k$ 个数字,方案数为 $0$。
  • 如果 $n=k$,表示每个数字都必须选,只有一种方案。
  • 否则,对于第 $n$ 位,我们可以选择不选或者选这一位的数字。如果不选,方案数为 count(n-1, k);如果选,方案数为 count(n-1, k-1)。所以总方案数为两者之和,即 count(n-1, k) + count(n-1, k-1)

最终返回 count(n, k) 即可。

动态规划

动态规划的思路与递归类似,但是可以避免重复计算。具体实现过程如下:

我们定义一个二维数组 dp[n+1][k+1],其中 dp[i][j] 表示一个 $i$ 位的二进制数中选出若干个位置,数值之和为 $j$ 的方案数。

根据上述递归过程,我们可以得到如下状态转移方程:

$$dp[i][j] = dp[i-1][j] + dp[i-1][j-1]$$

其中,$dp[i-1][j]$ 表示不选第 $i$ 位的数字,$dp[i-1][j-1]$ 表示选第 $i$ 位的数字。

最终,我们返回 dp[n][k] 即可。

代码实现
递归
def count(n, k):
    if k == 0:
        return 1
    if n < k:
        return 0
    if n == k:
        return 1
    return count(n-1, k) + count(n-1, k-1)
动态规划
def count(n, k):
    dp = [[0 for j in range(k+1)] for i in range(n+1)]
    for i in range(n+1):
        dp[i][0] = 1
    for i in range(1, n+1):
        for j in range(1, k+1):
            dp[i][j] = dp[i-1][j] + dp[i-1][j-1]
    return dp[n][k]
总结

这道题可以用递归或者动态规划来解决,但是需要注意当 $n<k$ 时要特判,方案数为 $0$。另外,使用动态规划时需要额外注意初始化 dp[0][j] = 0