把 n 拆成 k 份不计顺序的正整数:按最小值是否为 1 二分得到 p_k(n)=p_{k-1}(n-1)+p_k(n-k),记忆化搜索 O(nk) 计数。
OJ: roj
题目 ID: 3509
难度:普及-
标签:动态规划记忆化搜索整数分拆python
创建: 2026-10-02 04:40
更新: 2026-10-06 12:31
题目描述
将整数
思路
设
参考代码
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-10-02 04:40
# update_at: 2026-10-02 04:40
import sys
from functools import cache
@cache
def ways(n: int, k: int) -> int:
"""把 n 分成 k 份非空、不计顺序的正整数方案数(约定每份从小到大)。"""
if n < k: # 每份至少 1,k 份最少也要 k
return 0
if k == 1: # 只剩一份,唯一分法就是 n 本身
return 1
# 按"是否存在等于 1 的一份"分类:
# 有:去掉这份 1,变成 ways(n-1, k-1);
# 没有:每份都 ≥2,每份先扣掉 1,变成 ways(n-k, k)。
return ways(n - 1, k - 1) + ways(n - k, k)
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n, k = next(data), next(data)
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-06 12:31
* update_at: 2026-10-06 12:31
*/
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll n, k;
ll memo[205][7]; // memo[i][j] 表示把 i 分成 j 份的方案数
// 把 total 分成 parts 份非空、不计顺序的正整数方案数
ll ways(ll total, ll parts) {
if (total < parts) return 0; // 每份至少 1,总和小于份数则无解
if (parts == 1) return 1; // 只剩一份,唯一分法就是 total 本身
if (memo[total][parts] != -1) return memo[total][parts];
// 按是否有一份等于 1 分类:有则去掉一个 1;没有则每份先减 1
memo[total][parts] = ways(total - 1, parts - 1) + ways(total - parts, parts);
return memo[total][parts];
}
int main() {
memset(memo, -1, sizeof(memo));
scanf("%lld %lld", &n, &k);
printf("%lld\n", ways(n, k));
return 0;
}