一个正整数n可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中n1≥n2≥…≥nk,k≥1。
我们将这样的一种表示称为正整数n的一种划分。
现在给定一个正整数n,请你求出n共有多少种不同的划分方法。
相当于一个完全背包问题,因为每个数可以拿无数次
完全背包的状态转移
F【i】【j】=max(F【i-1】【j】,F【i】【j -w】
优化成一维 F【j】=max( F【j】,f【j- w】)顺序循环
一般计数类背包问题的核心就是初始化
f[i][j]表示只前 i 中选,且总和等于j的方案数
所以f[0][0]=1;
#include <iostream>
#include <algorithm>
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] << endl;
return 0;
}
另一种思路
F[i][j] 表示总和为 i,数 的 个数为 j 的方案数
分成两部分
一部分最小值为1 如果减去1个最小值 1 f[ i ] [ j ]=f[ i - 1][ j-1] 总和为i-1个数,数的个数为j-1
一部分最小值大于 1 如果将所有的 数 减去1 f[i][j] =f[ i - j ][ j ]
所以 f[i][j] = f[i - 1][j - 1] + f[i - j][j];
并且初始化
f[ 0 ][ 0] =1;
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, mod = 1e9 + 7;
int n;
int f[N][N];
int main()
{
cin >> n;
f[1][1] = 1;
for (int i = 2; i <= n; i ++ )
for (int j = 1; j <= i; j ++ )
f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod;
int res = 0;
for (int i = 1; i <= n; i ++ ) res = (res + f[n][i]) % mod;
cout << res << endl;
return 0;
}
这个也是类似于计数的dp,其思想跟上面的第二种方法很像
首先定义状态 f[ i] [j] 表示总能量为 i 分身个数 为 j 的方案数
所求为 方案数
同样分成两部分
分身能量最小为 0 ,则减去最后的那个0能量的影分身 f [i] [j]= f[i ] [j-1]
分身能量最小不为 0 则对所有影分身能量减去1 f[i ] [j ] =f[ i- j] [ j] 注意此时 i要大于 j
对了 还有初始化 f[0][0] =1;这是值表示的是方案数!方案数!方案数!
#include<iostream>
#include<algorithm>
using namespace std;
const int N=11;
//int dp[N][N];
int main()
{
int T;
cin>>T;
while(T--)
{
int dp[N][N]={0};
dp[0][0]=1;
int m,n;
cin>>m>>n;
for(int i=0;i<=m;i++)
for(int j=1;j<=n;j++)
{
dp[i][j]=dp[i][j-1];
if(i>=j)dp[i][j]+=dp[i-j][j];
}
cout<<dp[m][n]<<endl;
}
}