题意:给4个数n,r,k,m,求从n中选r个数(至少间隔k )并将这r个数分成至多m组一共有多少种情况。
输入为5 2 3 2时 Sample input means you can choose 1 and 4,1 and 5,2 and 5 in the same day. And you can make the machines in the same group or in the different group. So you got 6 schemes. 1 and 4 in same group,1 and 4 in different groups. 1 and 5 in same group,1 and 5 in different groups. 2 and 5 in same group,2 and 5 in different groups. We assume 1 in a group and 4 in b group is the same as 1 in b group and 4 in a group.(注意)
很简单的组合数学加递推题
n个数中取r个使其中间隔至少为k可以用插板法:先将r个球排好并在每两个相邻数之间插入k-1个球,然后将剩下的n-r-(k-1)*(r-1)个板插入到那些球之间,其实只要在n个球中选n-r-(k-1)*(r-1)个作为板就行了。总可能情况数为 c(n,n-r-(r-1)*(k-1))
将r个数分成最多m组推出递推关系:用dp[r][m]代表r个数最多分成m组有多少种情况,rec[r][m]代表r的数恰好分成m组有多少种情况
则 dp[r][m]=rec[r][0]+rec[r][1]+......rec[r][m]
rec[r][m] = rec[r-1][m-1]+(r-1)*rec[r-1][m] (若第r个数另成一组有rec[r-1][m-1]种情况,若将第r个数插入前面的组中有(r-1)*rec[r-1][m]种情况
#include <iostream>
#include <memory.h>
#include <cstdio>
using namespace std;
const long long MOD = 1000000007;
long long dp[1005][1005];
int com[2015][2015];
long long rec[1005][1005];
void init()
{
memset(rec,0,sizeof(rec));
memset(com,0,sizeof(com));
memset(dp,0,sizeof(dp));
com[0][0] = 1;
for(int i=1;i<=2010;i++)
{
com[i][0] = com[i][i] = 1;
for(int j=1;j<i;j++)
{
com[i][j]=com[i-1][j-1]+com[i-1][j];
com[i][j] %= MOD;
}
}
rec[0][0] = 1;
for(int i=1;i<=1002;i++)
{
for(int j=1;j<=1002;j++)
{
rec[i][j] = rec[i-1][j-1]+j*rec[i-1][j]%MOD;
rec[i][j] %= MOD;
dp[i][j] = dp[i][j-1]+rec[i][j];
dp[i][j] %= MOD;
}
}
}
int main()
{
int n,r,k,m,tmp;
init();
while(scanf("%d%d%d%d",&n,&r,&k,&m)!=EOF)
{
tmp = n-r-(r-1)*(k-1);
if(tmp<0) printf("0\n");
else
{
printf("%I64d\n",(long long)com[tmp+r][r]*dp[r][m]%MOD);
}
}
return 0;
}