【动态规划】整数划分及其变种

文章介绍了三种动态规划问题:1.整数划分,使用完全背包策略解决不可重复且考虑顺序的情况;2.鸣人的影分身问题,即可重复且不考虑顺序的完全背包问题;3.数的划分,是可重复但要求每份大于等于1的划分问题。每个问题都给出了相应的递推公式和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 整数划分【不可重复,考虑顺序,完全背包】

900. 整数划分
在这里插入图片描述

思路

题意转为完全背包求解。

等价于背包容量为 n n n,且有n个物品体积分别是 1 , 2 , 3... n 1,2,3...n 123...n ,每个数任意选无限次,问恰好装满背包的方案数

  • f [ i ] [ j ] f[i][j] f[i][j]: 表示前 i i i个数凑成体积为 j j j的方案数。

推导如下:

  1. 当前不选, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i - 1][j] f[i][j]=f[i1][j]
  2. 当前 i i i 选, f [ i ] [ j ] = f [ i − 1 ] [ j − i ] + f [ i − 1 ] [ j − 2 ∗ i ] + . . . f[i][j] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ... f[i][j]=f[i1][ji]+f[i1][j2i]+...

即当前物品从0个选到上限个:
=> f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i − 1 ] [ j − i ] + f [ i − 1 ] [ j − 2 ∗ i ] + . . . f[i][j] = f[i - 1][j]+f[i-1][j-i]+f[i-1][j-2*i]+... f[i][j]=f[i1][j]+f[i1][ji]+f[i1][j2i]+...

  • 又由 f [ i ] [ j − i ] = f [ i − 1 ] [ j − i ] + f [ i − 1 ] [ j − 2 ∗ i ] + . . . f[i][j - i] = f[i-1][j-i]+f[i-1][j-2*i]+... f[i][ji]=f[i1][ji]+f[i1][j2i]+... 可知:

=> f [ i ] [ j ] = f [ i ] [ j − i ] + f [ i − 1 ] [ j ] f[i][j] = f[i][j-i]+f[i-1][j] f[i][j]=f[i][ji]+f[i1][j]

代码

int n; cin >> n;
for (int i = 0; i <= n; i++) f[i][0] = 1;
for (int i = 1; i <= n; i++){
    for (int j = 1; j <= n; j++) {
        f[i][j] = f[i - 1][j] % mod;
        if(j >= i) f[i][j] = (f[i - 1][j] + f[i][j - i]) % mod;
    }
}
cout << f[n][n] << endl;
  • 压缩一维
g[0] = 1;
for (int i = 1; i <= n; i++)
    for (int j = i; j <= n; j++) 
        g[j] = (g[j] + g[j - i]) % mod;
cout << g[n] << endl;

2. 鸣人的影分身【可重复,不考虑顺序,>=0】

1050. 鸣人的影分身

在这里插入图片描述

思路

可重复型 & 二维费用完全背包

题意: n n n 拆成 m m m 份的方案数,换句话说就是用 m m m 个数拼成 n n n 的方案数。

每个数可以选多次( ≥ 0 \geq 0 0),就是完全背包问题,受体积和份数限制,即二维费用完全背包。

  • f [ i ] [ j ] f[i][j] f[i][j]: 表示体积为 i i i 时,划分 j j j 份的方案数。

推导如下:

  1. 当前有0的情况: f [ i ] [ j ] = f [ i ] [ j − 1 ] f[i][j]=f[i][j-1] f[i][j]=f[i][j1]
  2. 当前无0的情况: f [ i ] [ j ] = f [ i − j ] [ j ] f[i][j] = f[i-j][j] f[i][j]=f[ij][j]

=> f [ i ] [ j ] = f [ i ] [ j − 1 ] + f [ i − j ] [ j ] f[i][j] = f[i][j - 1] + f[i - j][j] f[i][j]=f[i][j1]+f[ij][j]

代码

cin >> n >> m;
f[0][0] = 1;
for (int i = 0; i <= n; i++) 
    for (int j = 1; j <= m; j++) {
        f[i][j] = f[i][j - 1];
        if (i >= j) f[i][j] = (f[i][j - 1] + f[i - j][j]) % mod; 
    }
cout << f[n][m] << endl;

3. 数的划分【可重复,不考虑顺序,>=1】

[编程题]数的划分

在这里插入图片描述

思路

与上题不同的是,分的每份都不能为空,即 ≥ 1 \geq 1 1

  • f [ i ] [ j ] : f[i][j]: f[i][j]: 表示体积为 i i i 时,划分 j j j 份的方案数。

推导如下:

  1. 当前有1的情况: f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i1][j1]
  2. 当前无1的情况: f [ i ] [ j ] = f [ i − j ] [ j ] f[i][j] = f[i-j][j] f[i][j]=f[ij][j]

=> f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − j ] [ j ] f[i][j]=f[i-1][j-1]+f[i-j][j] f[i][j]=f[i1][j1]+f[ij][j]

实际上,和第二题一致。不过在当前至少一个 1 1 1 存在,递推时体积 − 1 -1 1;而第二题体在当前至少一个 0 0 0 存在,递推时体积 − 0 -0 0

代码

int n, m; cin >> n >> m;
vector<vector<int>> f(n + 1, vector<int>(m + 1, 0)); 
f[0][0] = 1;
const int mod = 1e9 + 7;
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= i && j <= m; j++) {
        f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod;
    }
}
cout << f[n][m] << endl;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღCauchyོꦿ࿐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值