自然数的拆分(背包)

问题 AO: 【基础】自然数的拆分方案总数<

题目描述

给定一个自然数N,要求把N拆分成若干个正整数相加的形式,参与加法运算的数可以重复。

注意:

  1. 拆分方案不考虑顺序,也就是3=1+2和3=2+1算作相同的方案;
  2. 至少拆分成2个数的和。
  3. 求拆分的方案数 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+22+1 视为同一种方案)。

可以转换为:

  • 选取若干个 1 到 N-1 的数,使其和等于 N,且每个数可以被重复选取

2. 动态规划(DP)解法

定义状态

dp[i][j] 表示使用数字 1i 进行拆分,得到和为 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;
}
posted @   bakul  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示