P1025 数的划分
题目描述:
将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;
1,5,1;
5,1,1.
问有多少种不同的分法。
输入格式
n,k (6<n≤200,2≤k≤6)
输出格式
1个整数,即不同的分法。
集合表述:dp[ i ][ j ] 表示整数 i 分为 j 份的方案数;
集合划分:根据 dp[i][j] 表示的方案内含1 和不含1 分为两类;
dp[ i-1 ][ j-1 ] 为含1的方案数(假设第 j 个数为1;
dp[ i-j ][ j ] 为不含1的方案数(j个数都减1;
状态转移方程:dp[i][j] = dp[i-1][j-1] + dp[i-j][j]
边界处理:dp[0][0] = 1;
#include<iostream>
#include<cstring>
using namespace std;
int dp[201][7];
int main()
{
int n,k;
cin>>n>>k;
dp[0][0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=k && j<=i;j++){
dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
}
}
cout<<dp[n][k]<<endl;
return 0;
}
900. 整数划分
题目描述:
一个正整数n可以表示成若干个正整数之和,形如:n=n1+n2+…+nk,其中n1≥n2≥…≥nk,k≥1。
我们将这样的一种表示称为正整数n的一种划分。
现在给定一个正整数n,请你求出n共有多少种不同的划分方法。
1≤n≤1000
解法1、
集合表述:dp[ i ][ j ] 表示在1~i 中 选取任意个整数 且其和为 j 的方案数;
集合划分:其状态表示和完全背包基本相似,所以我们同样根据第 i 个数选取的个数来划分集合。
dp[ i-1 ][ j ] 表示不选第 i 个数的方案数;
dp[ i-1 ][ j-k * i ] 表示选 k个 i 的方案数;
状态转移方程: dp[ i ][ j ] = dp[ i-1 ][ j ] + dp[ i-1 ][ j-k * i ] ;
边界处理:dp[i][0] = 1;
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010;
const int mod = 1e9+7;
int dp[N][N];
int main()
{
int n;
cin>>n;
for(int i=0;i<=n;i++) dp[i][0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=0;k*i<=j;k++){
dp[i][j] = (dp[i][j] + dp[i-1][j-k*i])%mod;
}
}
}
cout<<dp[n][n]<<endl;
return 0;
}
同样地可以类比完全背包进行优化(不优化也可;
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010;
const int mod = 1e9+7;
int dp[N];
int main()
{
int n;
cin>>n;
dp[0] = 1;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
dp[j] = (dp[j] + dp[j-i])%mod;
}
}
cout<<dp[n]<<endl;
return 0;
}
解法2、
也可以类比上一个问题设集合:
集合表述:dp[ i ][ j ] 为将整数 i 划分成 j 份的方案数;
集合划分:根据方案中最小的数分为两类:最小数为1 和 最小数大于1;
dp[ i-1 ][ j-1 ] 为含1的方案数(假设第 j 个数为1;
dp[ i-j ][ j ] 为不含1的方案数(j个数都减1;
状态转移方程:dp[i][j] = dp[i-1][j-1] + dp[i-j][j]
边界处理:dp[0][0] = 1;
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1010;
const int mod = 1e9+7;
int dp[N][N];
int main()
{
int n;
cin>>n;
dp[0][0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i][j] = (dp[i-1][j-1]+dp[i-j][j]) % mod;
}
}
int ans=0;
for(int i=0;i<=n;i++) ans = (ans + dp[n][i]) % mod; //所有 dp[n][i] 的和即为答案
cout<<ans<<endl;
return 0;
}