1. 程式人生 > >盒子與小球之三

盒子與小球之三

can 註釋 deb CM tor http names 維護 我們

描述

有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\n",ways[M][N]); return 0; }

參考文獻

[1]https://blog.csdn.net/TSOI_Vergil/article/details/53102437

盒子與小球之三