题目:http://www.gdfzoj.com/oj/contest/475/problems/3
我们有n个相同的弹珠,k个相同的盒子.现在随机的将每个弹珠扔进盒子中,使得最终每个盒子非空,求出一共有多少种不同的方案.
两种方案不同当且仅当将盒子中的弹珠数最小表示后不同.
由于方案数可能非常多,答案对998244353取模
输入
7 3
输出
4
很容易想到dp,也容易想到f[i,j]:i个弹珠分j份,也容易想到跟f[i-1,j-1]有关
但是怎么推公式呢?
首先看一下题目:题目要求方案不考虑顺序,所以我们尽量把处理的k位是最小的即从小到大(防止重复)
就按样例枚举:f[7,3]=4 :1/1/5 1/2/4 1/3/3 2/2/3
对应 f[6,2]=3:1/5 2/4 3/3
那剩下的1个怎么办?
经观察剩下的是 2/2/3 ,但你又不能+f[5,2] ,会出现重复
仔细想想,f[6,2]对应的最小值是1(保证了顺序不会重复),那么剩余的就是>=2的,但又不能出现1
那我们就把所有j份都-1,这样不就可以出现1咯
即+f[4,3]:1/1/2 >>所有+1>>2/2/3 咯
所以f[i,j]=f[i-1,j-1]+f[i-j,j]
f[7,3]例子不是特别好(考试时要自己多出些数据,样例不一定是最好的),还是不懂可以枚举f[10,3]
f[10,3]=8 : 1/1/8 1/2/7 1/3/6 1/4/5 2/2/6 2/3/5 2/4/4 3/3/4
f[9,2]=4 : 1/8 2/7 3/6 4/5
剩下的4个是 2/2/6 2/3/5 2/4/4 3/3/4
这些的共同特点就是最小值都>=2
为了良好利用上f[i,j],把所有盒子里的弹珠都-1,最小值>=1 ,满足f[i,j]
所以剩下4=f[i-j,j]=f[7,3] : 1/1/5 1/2/4 1/3/3 2/2/3
所以。。。
注意要取模!!!!!!!!!
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxS=5000,value=998244353;
long long f[maxS+5][maxS+5];
int main()
{
int i,n,m,j;
freopen("a.txt","r",stdin);
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
for (j=1;j<=min(i,m);j++)
{
if (i==j)
f[i][j]=1;
else
f[i][j]=(f[i-1][j-1]+f[i-j][j])%value;
}
printf("%lld\n",f[n][m]);
return 0;
}