📜  门| GATE-CS-2001 |问题29(1)

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

题目描述

给定 $n$ 个整数的集合 $S$ 和一个定值 $T$,请在 $S$ 中找出一些数字,使得这些数字的和恰好为 $T$。

输入格式

第一行包含整数 $n$ 和 $T$。

第二行包含 $n$ 个不同的数字,每个数字之间用空格隔开。

输出格式

按从小到大的顺序输出所求的子集中的数字,数字之间用空格隔开。如果有多解,请输出任意一个。如果无解,则输出 No Solution

样例

输入:

9 100
75 34 56 12 77 40 98 23 45

输出:

23 34 43
解题思路

题目要求求解集合 $S$ 中和为 $T$ 的子集,这种问题称为子集和问题(Subset Sum Problem)。

只考虑和为 $T$ 的子集数量有多少种,可以使用动态规划求解。

设 $f(i, j)$ 表示只考虑前 $i$ 个数,且和恰好为 $j$ 的方案数。则有:

$$ f(i, j) = \begin{cases} 1, & j=0 \ 0, & i=0 \ f(i-1, j) + f(i-1, j-s_i), & j\ge s_i \ f(i-1, j), & j<s_i \end{cases} $$

其中 $s_i$ 表示第 $i$ 个数字。

动态规划求解可以优化成空间复杂度为 $O(T)$ 的形式。

代码实现
def subset_sum(n: int, T: int, S: List[int]) -> List[int]:
    f = [0] * (T + 1)
    f[0] = 1
    for i in range(1, n + 1):
        for j in range(T, S[i - 1] - 1, -1):
            f[j] += f[j - S[i - 1]]
    if f[T]:
        res = []
        j = T
        for i in range(n, 0, -1):
            if j >= S[i - 1] and f[j - S[i - 1]]:
                res.append(S[i - 1])
                j -= S[i - 1]
        return res[::-1]
    else:
        return ['No Solution']

时间复杂度:$O(NT)$,其中 $N$ 是数字的个数,$T$ 是目标和。