自然数的拆分(背包)
问题 AO: 【基础】自然数的拆分方案总数<
题目描述
给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。
注意:
- 拆分方案不考虑顺序,也就是3=1+2和3=2+1算作相同的方案;
- 至少拆分成2个数的和。
- 求拆分的方案数 mod 2147483648的结果。
输入
一个自然数N。(1≤N≤4000)
输出
输入一个整数,表示结果。
样例输入
7
样例输出
14
提示
【举例】按题意,数字5有6种不同的拆分方案,分别是
1+1+1+1+1
1+1+1+2
1+1+3
1+4
1+2+2
2+3
分析
我怎么就找不到正确的状态呢???
这个问题是整数拆分的变形,可以用动态规划(DP)来解决。我们可以用完全背包的思想来理解它。
1. 问题分析
对于给定的 N
,要求将 N
拆分成至少两个正整数之和,且拆分顺序不影响结果(即 1+2
和 2+1
视为同一种方案)。
可以转换为:
- 选取若干个 1 到 N-1 的数,使其和等于 N,且每个数可以被重复选取。
2. 动态规划(DP)解法
定义状态
设 dp[i][j]
表示使用数字 1
到 i
进行拆分,得到和为 j
的方案数。
-
状态转移方程
- 不选
i
,则dp[i][j] = dp[i-1][j]
- 选
i
,则dp[i][j] = dp[i][j-i]
综上:
[
dp[i][j] = dp[i-1][j] + dp[i][j-i]
]解释:
dp[i-1][j]
表示不使用i
进行拆分的方案数。dp[i][j-i]
表示选取i
之后,剩下的j-i
仍然需要拆分,继续使用i
及更小的数。
- 不选
初始化
dp[i][0] = 1
,表示拆分成0
的方案数只有一种,即空集。dp[0][j] = 0
,没有数可用时,不能拆分。
3. 代码实现
我们可以用一维数组优化 DP,把 dp[i][j]
压缩成 dp[j]
(完全背包思想)。
#include <iostream>
using namespace std;
const int MOD = 2147483648;
const int N = 4005;
int dp[N];
int main() {
int n;
cin >> n;
dp[0] = 1; // 拆分成 0 的方案只有一种
for (int i = 1; i < n; i++) { // 遍历 1~(N-1) 作为可选数
for (int j = i; j <= n; j++) { // 这里采用正序遍历,类似完全背包
dp[j] = (dp[j] + dp[j - i]) % MOD;
}
}
cout << dp[n] << endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战