盒子與小球系列題解
盒子與小球,NOI網站題庫的一系列題,可在NOI題庫中提交。
盒子與小球二:N個有差別的盒子(1<=N<=20)。你有A個紅球和B個藍球。0 <= A <= 15, 0 <= B <= 15。球除了顏色沒有任何區別。你可以將球放進盒子。一個盒子可以同時放進兩種球,也可以只放一種,也可以空著。球不必全部放入盒子中。程式設計計算有多少种放置球的方法。
我們可以設計一個狀態,F[i][j][k]表示前i個盒子中放了A個紅球,B個藍球的方案數,那麼考慮轉移,對於當前這個盒子,我們可以放若干個紅球和若干個藍球,即F[i][j][k]=西格瑪(F[i-1][j-l][k-w]),其中l<=j,k<=w,這樣狀態量N^3,轉移N^2,總複雜度為N^5,可以過掉,不過我還看到有更好的方法,在提問裡。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; long long f[22][16][16],ans; int n,A,B; int main() { f[0][0][0]=1; scanf("%d%d%d",&n,&A,&B); for (int i=1;i<=n;i++) for (int j=0;j<=A;j++) for (int k=0;k<=B;k++) { for (int l=0;l<=j;l++) for (int w=0;w<=k;w++) f[i][j][k]+=f[i-1][j-l][k-w]; // printf("%d %d %d %d\n",i,j,k,f[i][j][k]); } for (int j=0;j<=A;j++) for (int k=0;k<=B;k++) ans+=f[n][j][k]; printf("%lld\n",ans); return 0; }
盒子與小球三:有N個相同的球,M個不同的盒子,每個盒子最多放K個球 ,請計算將這N個球全部放入盒子中的方案數模1000007後的結果 ,N<=5000,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)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 5005
#define mod 1000007
int f[maxn][maxn];
int n,m,k,sum;
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=0;i<=m;i++) f[i][0]=1;
for (int i=1;i<=m;i++)
{
sum=i;
for (int j=1;j<=n;j++)
{
/* for (int l=0;l<=k;l++)
{
if (j<l) break;
f[i][j]=(f[i][j]+f[i-1][j-l])%mod;
}*/
f[i][j]=sum;
sum=(sum+f[i-1][j+1])%mod;
if (j-k>=0) sum=(sum-f[i-1][j-k]+mod)%mod;
// printf("%d %d %d\n",i,j,f[i][j]);
}
}
printf("%d\n",f[m][n]);
return 0;
}
盒子與小球四:給定N個各不相同的小球,和M個不同的BOX,有多少種不同的放球方法,使得每個BOX裡的小球個數不小於K。N,M,K均小於15。
這道題的小球也不相同了,那麼我們考慮用隔板法,我們可以先預設小球是一樣的,爆搜出所有隔板的組合,當我們知道了每個盒子裝幾個小球后,我們有一個公式:有n1個a1,n2個a2....ni個ai,不同的排列數為(n1+n2+..+ni)!/(n1!+n2!+....+ni!),這樣只要我們爆搜出所有的隔板組合,我們就能O(1)的知道這種組合的方案數,對於K=0的特殊情況,我們沒有了任何限制,那麼每個球都有M種選擇,那麼答案就是M^N。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,r,k;
long long c[20],jj[20],ans;
void dfs(int last,int cnt)
{
if (cnt==r)
{
long long temp=1,res=n;
for (int i=1;i<cnt;i++)
{
temp=temp*jj[c[i]];
res-=c[i];
}
temp*=jj[res];
if (res<k) return;
ans+=(jj[n]/temp);
return;
}
for (int i=last;i<n;i++)
{
c[cnt]=i-last+1;
if (c[cnt]<k) continue;
dfs(i+1,cnt+1);
}
}
int main()
{
jj[0]=1;
for (int i=1;i<=15;i++) jj[i]=jj[i-1]*i;
while (1)
{
ans=0;
scanf("%d%d%d",&n,&r,&k);
if (n==0&&r==0&&k==0) break;
if (k==0)
{
ans=1;
for (int i=1;i<=n;i++) ans=ans*r;
printf("%lld\n",ans);
continue;
}
dfs(1,1);
printf("%lld\n",ans);
}
return 0;
}