动态规划整数划分及其变种
Posted ღCauchyོꦿ࿐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划整数划分及其变种相关的知识,希望对你有一定的参考价值。
文章目录
1. 整数划分【不可重复,完全背包】
思路
题意转为完全背包求解。
等价于背包容量为 n n n,且有n个物品体积分别是 1 , 2 , 3... n 1,2,3...n 1,2,3...n ,每个数任意选无限次,问恰好装满背包的方案数。
- f [ i ] [ j ] f[i][j] f[i][j]: 表示前 i i i个数凑成体积为 j j j的方案数。
推导如下:
- 当前不选, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j] = f[i - 1][j] f[i][j]=f[i−1][j]
- 当前 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[i−1][j−i]+f[i−1][j−2∗i]+...
即当前物品从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[i−1][j]+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][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 ] [ 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][j−i]+f[i−1][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】
思路
可重复型 & 二维费用完全背包
题意: 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 份的方案数。
推导如下:
- 当前有0的情况: f [ i ] [ j ] = f [ i ] [ j − 1 ] f[i][j]=f[i][j-1] f[i][j]=f[i][j−1]
- 当前无0的情况: f [ i ] [ j ] = f [ i − j ] [ j ] f[i][j] = f[i-j][j] f[i][j]=f[i−j][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][j−1]+f[i−j][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的情况: f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] f[i][j] = f[i - 1][j - 1] f[i][j]=f[i−1][j−1]
- 当前无1的情况: f [ i ] [ j ] = f [ i − j ] [ j ] f[i][j] = f[i-j][j] f[i][j]=f[i−j][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[i−1][j−1]+f[i−j][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;
以上是关于动态规划整数划分及其变种的主要内容,如果未能解决你的问题,请参考以下文章
算法动态规划 ④ ( 动态规划分类 | 坐标型动态规划 | 前缀划分型动态规划 | 前缀匹配型动态规划 | 区间型动态规划 | 背包型动态规划 )
算法动态规划 ④ ( 动态规划分类 | 坐标型动态规划 | 前缀划分型动态规划 | 前缀匹配型动态规划 | 区间型动态规划 | 背包型动态规划 )