按最后一个盘子是否为空做不重不漏二分:空则少一个盘子,非空则每个盘子各垫一个苹果,得 f(m,n)=f(m,n-1)+f(m-n,n),记忆化求整数拆分数 p_{≤n}(m)。
OJ: roj
题目 ID: 1192
难度:普及-
标签:入门动态规划计数DP记忆化搜索python
创建: 2026-09-29 22:50
更新: 2026-10-05 05:11
题目描述
把 1 与 7 3 对应输出 8。
思路
设
参考代码
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-07-05 22:10
# update_at: 2026-07-05 22:10
import sys
from functools import cache
@cache
def count(m: int, n: int) -> int:
"""m 个同样的苹果放进 n 个同样的盘子(允许空盘)的方案数。
边界:没有苹果、或只剩一个盘子,都只有一种放法。
"""
if m == 0 or n == 1:
return 1
if m < n:
return count(m, m) # 盘子比苹果多,多出的盘子必然空着,与 m 个盘子等价
# 按第 n 个盘子是否为空分类:空 → 少一个盘子;不空 → 每盘先垫 1 个
return count(m, n - 1) + count(m - n, n)
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
T = next(data)
out: list[str] = []
for _ in range(T):
m, n = next(data), next(data) # 苹果数 m,盘子数 n
out.append(str(count(m, n)))
print('\n'.join(out))
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 05:11
* update_at: 2026-10-05 05:11
*/
#include <cstdio>
typedef long long ll;
ll memo[15][15]; // memo[m][n] = m 个苹果放进 n 个盘子的分法数,-1 表示还没算过
// 求把 m 个同样的苹果放进 n 个同样的盘子(允许空盘)的分法数
ll count_ways(ll m, ll n) {
if (memo[m][n] != -1) return memo[m][n]; // 记忆化:每个状态只算一次
ll res;
if (m == 0 || n == 1) {
res = 1; // 没有苹果(全空)或只剩一个盘子,都只有一种放法
} else if (m < n) {
res = count_ways(m, m); // 盘子比苹果多,多出的盘子必然空着,与 m 个盘子等价
} else {
// 按第 n 个盘子是否为空分类,两类互斥且不漏:
// 空 -> 少一个盘子:count_ways(m, n-1)
// 非空 -> 非降序下所有盘子都非空,每盘先垫 1 个苹果:count_ways(m-n, n)
res = count_ways(m, n - 1) + count_ways(m - n, n);
}
memo[m][n] = res;
return res;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
ll m, n;
scanf("%lld %lld", &m, &n);
// 多组数据共用一张记忆化表:先把会用到的范围清成 -1
for (ll i = 0; i <= m; ++i)
for (ll j = 1; j <= n; ++j)
memo[i][j] = -1;
printf("%lld\n", count_ways(m, n));
}
return 0;
}