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

Posted ღCauchyོꦿ࿐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划整数划分及其变种相关的知识,希望对你有一定的参考价值。

文章目录

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;

以上是关于动态规划整数划分及其变种的主要内容,如果未能解决你的问题,请参考以下文章

98 数的划分

98 数的划分

动态规划3--分割整数

动态规划_百炼 4117 简单的整数划分问题

算法动态规划 ④ ( 动态规划分类 | 坐标型动态规划 | 前缀划分型动态规划 | 前缀匹配型动态规划 | 区间型动态规划 | 背包型动态规划 )

算法动态规划 ④ ( 动态规划分类 | 坐标型动态规划 | 前缀划分型动态规划 | 前缀匹配型动态规划 | 区间型动态规划 | 背包型动态规划 )