划分数:有n个无区别的物品,将它们划分成不超过m组,求出划分方法数模M的余数。
这样的划分叫做n的m划分,特别的,m=n时称作n的划分数。DP不仅对于求解最优问题有效,对于各种排列组合的个数,概率或者期望之类的计算同样很有用。
定义:
dp[i][j]=j的i划分的总数
如果将j分划成i个的话,可以先取出k个,然后将剩余的j-k个分成i-1份,此时得到的递推式时这样的:
但是这样递推是不正确的,会有很多重复,例如1+1+2和1+2+1就被当作不同的划分数来计数了。为了不重复计数,需要找到别的递推公式
当j>=i时,dp[i][j]=dp[i-1][j](j的i-1划分,相当于当前位取0的情况)+dp[i][j-i](当前位不取0,先把每一位置为1,再将剩下的j-i分下去)
j<i时,dp[i][j]=dp[i-1][j](当前位只能取0)
附上代码:
int n,m;
int dp[MAXM+1][MAXN+1];
void solve()
{
dp[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=n;j++){
if(j-i>=0){
dp[i][j]=(dp[i-1][j]+dp[i][j-i])%mod;
}else{
dp[i][j]=dp[i-1][j];
}
}
}
printf("%d\n",dp[m][n]);
}
附上本题代码(使用两个long long数组进行拼接):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1100;
const ll inf=1000000000000000000LL;
ll a[MAXN];
ll b[MAXN];
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)!=EOF){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
b[0]=1;
for(int i=1;i<=k;i++){
for(int j=i;j<=n;j++){
a[j]=a[j]+a[j-i]+(b[j]+b[j-i])/inf;
b[j]=(b[j]+b[j-i])%inf;
}
}
if(a[n]==0){
printf("%I64d\n",b[n]);
}else{
printf("%I64d%018I64d\n",a[n],b[n]);
}
}
return 0;
}