1. 程式人生 > >盒子與小球系列題解

盒子與小球系列題解

     盒子與小球,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;	
}