盒子與小球之三
阿新 • • 發佈:2018-06-14
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 typedeflong 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
盒子與小球之三