题目:900. 整数划分 - AcWing题库
两种思路:Ⅰ、Ⅱ
Ⅰ
一状态表示f[i][j] 集合:从1~i中选,总体积(总和)恰好是j的选法集合。属性:选法的数量
状态计算:f[i][j],假设第i个数最多选s个,那么:
选0个:f[i - 1][j],选1个f[i - 1][j - i]...第s个f[i - 1][j - s * i],那么就有:
f[i][j] = f[i - 1][j] + f[i - 1][j - i] + ... +f[i - 1][j - s * i]
f[i][j - i]= f[i - 1][j - i] + ... +f[i - 1][j - s * i](无[j - (s+1)*i,因为总体积已经少了i)
那么就有f[i][j] = f[i - 1][j] + f[i][j - i]。
所需要的是当前的值,那么从小到大枚举即可。优化为一维:f[j] = f[j] + f[j - i]
上述也就是转化为了多重背包进行求解
代码:
二维
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N][N];
int main()
{
cin >> n;
//恰好是0的情况只有数本身,所以划分法为1
for(int i = 1; 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][j] + f[i][j - i]) % mod;
}
cout << f[n][n];
return 0;
}
一维:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N];
int main()
{
cin >> n;
f[0] = 1;
for(int i = 1; i <= n; i ++ )
for(int j = i; j <= n; j ++ )
f[j] = (f[j] + f[j - i]) % mod;
cout << f[n];
return 0;
}
Ⅱ
一、状态表示f[i][j] 集合:所有总和是i, 并且恰好表示成j个数的方案。属性:数量
二、根据方案里是否有1来进行划分。
①方案里含有1:f[i - 1][j - 1]。方案个数减去1,那么总和也减去1,二者等价
②方案里不含1:f[i - j][j]。该方案的每一个数都减去1
代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N][N];
int main()
{
cin >> n;
//总和为0时,方案数为1
f[0][0] = 1;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= i; j ++ )
f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod;
//扫一遍总和为n,分成1~n的方案数量的总和
int res = 0;
for(int i = 1; i <=n; i ++ ) res =(res + f[n][i]) % mod;
cout << res ;
return 0;
}