盒子与小球系列题解

Posted TSOI_Vergil

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了盒子与小球系列题解相关的知识,希望对你有一定的参考价值。

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


以上是关于盒子与小球系列题解的主要内容,如果未能解决你的问题,请参考以下文章

小球与盒子的故事

「题解」「HDU3037」Saving Beans

小球与盒子(组合数学基本模板)

盒子与小球之二

盒子与小球之二

●BZOJ 4710 [Jsoi2011]分特产