Two Sets II 题解

Two Sets II 题解

题目描述

https://cses.fi/problemset/task/1092

给定数字 1, 2, …, n,求将其分成两个和相等集合的方式数。

限制: - 1 ≤ n ≤ 500

示例: - 输入:7 - 输出:4 - 解释: - {1,3,4,6} 和 {2,5,7} - {1,2,5,6} 和 {3,4,7} - {1,2,4,7} 和 {3,5,6} - {1,6,7} 和 {2,3,4,5}


解法

问题类型:0-1 背包(每个数字只能用一次,求方案数)

分析: - 数字 1~n 的总和为 S = n(n + 1)/2 - 如果 S 为奇数,无法平分,输出 0 - 如果 S 为偶数,目标是在一半和 S/2 内放入一些数字

定义dp[j] 为凑成和 j 的方式数

初始条件dp[0] = 1(和为 0 有一种方式:什么都不选)

状态转移dp[j] = dp[j] + dp[j − i]  for all i (倒序)

解释:遍历数字 1~n-1(最后数字 n 必须放在较大的一组,所以只用 1~n-1),尝试将每个数字加入集合中。倒序遍历确保每个数字只用一次。

代码

import sys

MOD = 10**9 + 7

n = int(input())

sum_elem = n * (n + 1) // 2

if sum_elem % 2 == 1:
    print(0)
    sys.exit()

target = sum_elem // 2

dp = [0] * (target + 1)
dp[0] = 1

for i in range(1, n):
    for j in range(target, i - 1, -1):
        dp[j] = (dp[j] + dp[j - i]) % MOD

print(dp[target])

时间复杂度:O(n × target) 空间复杂度:O(target)

注意:只需遍历 1~n-1,因为数字 n 必须放在较大的一组(如果总和是偶数,较大的那组一定包含 n)。


算法详解

为什么只用 1~n-1?

设总和为 S = n(n + 1)/2,目标值为 S/2

  • 假设 n 在较小的那组,那么较小那组的最大可能和为 1 + 2 + ... + (n − 1) = n(n − 1)/2
  • 由于 S/2 = n(n + 1)/4 > n(n − 1)/4,所以 n 不可能在较小的那组
  • 因此 n 一定在较大的那组,只需用 1~n-1 来凑成 S/2

示例

n = 7,总和 = 28,目标 = 14

用数字 1~6 凑成 14 的方式:

{1,3,4,6} = 14
{1,2,5,6} = 14
{1,2,4,7} 错误(7 超出范围)
{1,6,7}    错误(7 超出范围)

实际上用 1~6 凑 14 的 4 种方式,加上数字 7,就是 4 种分组方式。


Two Sets II 题解
https://mingsm17518.github.io/2026/04/08/algorithm/03_Gold/02_动态规划/0-1背包/09_Two_Sets_II/
作者
Ming
发布于
2026年4月8日
更新于
2026年4月8日
许可协议