把 n 拆成 k 个正整数且不计顺序,按「最小值是否为 1」分类得到 p_k(n)=p_{k-1}(n-1)+p_k(n-k),递推 O(nk) 求解。
OJ: roj
题目 ID: 1304
难度:普及-
标签:递推数学记忆化搜索python
创建: 2026-09-30 04:05
更新: 2026-10-05 08:50
题目描述
把整数 1,1,5、1,5,1、5,1,1 算同一种。输入一行两个整数 7 3 对应输出 4:1,1,5;1,2,4;1,3,3;2,2,3。
思路
设
参考代码
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-09-30 04:05
# update_at: 2026-09-30 04:05
import sys
from functools import cache
@cache
def ways(total: int, parts: int) -> int:
"""把 total 分成 parts 个正整数、不计顺序的方案数。
parts 份都要非空,所以 total < parts 无解;只剩一份时整块给它,只有 1 种。
任何方案按「最小值是否为 1」二分:最小值是 1 → 拿掉这个 1,
剩 ways(total-1, parts-1);最小值 ≥ 2 → 每份先垫掉 1,整体减 parts,
一一对应到 ways(total-parts, parts)。两类不重不漏。
"""
if total < parts:
return 0
if parts == 1:
return 1
return ways(total - 1, parts - 1) + ways(total - parts, parts)
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n, k = next(data), next(data) # 题面只有一组数据,6 < n <= 200,2 <= k <= 6
print(ways(n, k))
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-05 08:50
* update_at: 2026-10-05 08:50
*/
#include <cstdio>
typedef long long ll;
const int MAXN = 205; // n 的上界(6 < n <= 200)
const int MAXK = 10; // k 的上界(2 <= k <= 6)
ll f[MAXN][MAXK]; // f[i][j]:把 i 拆成 j 个正整数、不计顺序的方案数
int main() {
ll n, k;
scanf("%lld %lld", &n, &k);
// 边界:拆成 1 份只有一种(整块给它);份数比总和还大则无解
for (ll i = 0; i <= n; i++) {
f[i][1] = 1;
for (ll j = 2; j <= k; j++) {
f[i][j] = 0; // 先默认无解,保证 i < j 时取到 0
if (i >= j) {
// 按最小值是否为 1 分类:
// 最小值 = 1 → 删掉一个 1 → f[i-1][j-1]
// 最小值 ≥ 2 → 每份减 1 → f[i-j][j]
f[i][j] = f[i - 1][j - 1] + f[i - j][j];
}
}
}
printf("%lld\n", f[n][k]);
return 0;
}