- 描述
-
有N个相同的球,M个不同的盒子,每个盒子最多放K个球
请计算将这N个球全部放入盒子中的方案数模1000007后的结果 - 输入
-
三个正整数,依次为N,M,K
- 输出
-
输出方案数模1000007后的结果
- 样例输入
-
4 2 3
- 样例输出
-
3
- 提示
-
总共有3种方案,依次为
{ 3 , 1 }
{ 2 , 2 }
{ 1 , 3 }
对于100%的数据, N,M <= 5000
查看
相比之前的水题,这个题,对我来说就有点难了= =卡时间
我们可以设计状态F[i][j]表示前i个盒子放j个求有多少种方法,那么F[i][j]=西格玛(F[i-1][j-l]),l的范围是[0,k],状态量是N^2,转移是O(K),复杂度是O(N*M*K),TLE,我们考虑优化转移,我们发现其实l的范围是固定的,类似于一个滑动窗口,那么我们可以维护一个sum,sum=西格玛(F[i-1][j-l]),在j增加后,我们令sum=sum-F[i-1][j-k]+F[i-1][j+1],这样就做到了O(1)转移,时间复杂度O(N*M)[1]。
另外解释一下为什么sum的初始值是m。被注释掉的循环从0到n开始,n的初始值是1,那么和就是ways[m-1][0]+ways[m-1][1]。ways[m-1][0]=1,ways[m-1][1]=m-1
#include<cstdio> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<string> #include<map> #include<cstring> #define DEBUG(x) cout << #x << " = " << x << endl typedef long long ll; using namespace std; const int MAXN=5e3+10; const int MOD=1000007; int N,M,K; ll ways[MAXN][MAXN]; ///前m个盒子放入n个球的放法 int main() { // freopen("in.txt","r",stdin); scanf("%d %d %d",&N,&M,&K); ways[0][0]=1; for(int i=1;i<=M;i++){ ways[i][0]=1; ways[0][i]=0; } for(int m=1;m<=M;m++){ int sum=m; for(int n=1;n<=N;n++){ // int l=min(n,K); // int r=0; // for(int i=0;i<=l;i++){ // r=(r+ways[m-1][n-i])%MOD; // } ways[m][n]=sum; sum=(sum+ways[m-1][n+1])%MOD; if(n-K>=0)sum=(sum-ways[m-1][n-K]+MOD)%MOD; } } printf("%lld ",ways[M][N]); return 0; }
参考文献
[1]https://blog.csdn.net/TSOI_Vergil/article/details/53102437