盒子与小球系列题解
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;
以上是关于盒子与小球系列题解的主要内容,如果未能解决你的问题,请参考以下文章