将连续整数集合二等分转化为0-1背包计数DP,最后利用对称性除以2统计无序划分数。
OJ: roj
题目 ID: 2027
难度:普及-
标签:动态规划背包问题计数DP
创建: 2026-10-01 03:32
更新: 2026-10-06 09:51
题目描述
给定正整数 7,样例输出 4。
思路
总和
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-04-18 10:00
# update_at: 2026-04-18 10:00
import sys
def count_subsets(n: int, target: int) -> int:
"""计算从 1 到 n 中选取若干互不相同的数使其和为 target 的方案数。"""
dp = [0] * (target + 1)
dp[0] = 1
for num in range(1, n + 1):
for s in range(target, num - 1, -1):
dp[s] += dp[s - num]
return dp[target]
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
try:
n = next(data)
except StopIteration:
return
total_sum = n * (n + 1) // 2
# 和为奇数无法二等分;否则每个无序二划分对应含/不含某个元素的有序方案数的一半
if total_sum % 2 != 0:
print(0)
return
target = total_sum // 2
ans = count_subsets(n, target) // 2
print(ans)
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 09:51
* update_at: 2026-10-06 09:51
*/
#include <cstdio>
typedef long long ll;
const int MAXS = 800; // N 最大 39 时总和 N(N+1)/2 = 780,target 最大 390
ll n;
ll dp[MAXS]; // dp[j] 表示从 1..n 中选若干互不相同的数,和恰好为 j 的方案数
int main() {
scanf("%lld", &n);
ll sum = n * (n + 1) / 2; // 1..n 的总和
// 总和为奇数时无法二等分,方案数为 0
if (sum % 2 == 1) {
printf("0\n");
return 0;
}
ll target = sum / 2;
// 0-1 背包计数:dp[0] = 1 表示空集这一种取法
dp[0] = 1;
for (ll i = 1; i <= n; i++) {
// 倒序枚举容量,保证每个数字 i 只被选一次
for (ll j = target; j >= i; j--) {
dp[j] += dp[j - i];
}
}
// 每个无序划分 {A, B} 在 dp 中被统计了两次(A 当选中集 / B 当选中集),除以 2
printf("%lld\n", dp[target] / 2);
return 0;
}