盒子与小球之三

描述

有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

原文地址:https://www.cnblogs.com/MalcolmMeng/p/9180505.html