vijos1060 盒子(重庆一中高2018级信息学竞赛测验7) 解题报告

来源:互联网 发布:人工智能计算器3.8.1版 编辑:程序博客网 时间:2024/05/02 04:34
【问题描述】  
  
  n 个盒子排成一行(编号为1..n)。你有A个红球和B个蓝球。球除了颜色没有任何区别。你可以将球放进盒子。一个盒子可以同时放进两种球,也可以只放一种,也可以空着。球不必全部放入盒子中。编程计算有多少种放置球的方法。 
 
    
 【输入格式】  
  
  一行,n,A,B,用空格分开。
 
    
 【输出格式】  
   
  一行,输出放置方案总数。
 
    
 【输入样例】   
   
2 1 1
 
    
 【输出样例】  
   

 
    
 【样例解释】  
   
  用一对括号表示一个盒子,R表示红色,B表示蓝色,有如下9种方案:
   (  ), (  )
   (R ), (  )
   (B ), (  )
   (RB), (  )
   (R ), (B )
   (B ), (R )
   (  ), (R )
   (  ), (B )
   (  ), (RB) 
 
    
 【数据范围】  
   

1<=n<=20 , 0<=A<=15, 0<=B<=15

答案不超过2^64-1


做题思路(错解):拿到这道题,审题时不仔细,把一个盒子可以同时放进两种球,也可以只放一种看成一个盒子可以同时放进两个球,也可以只放一个,然后就以为一个盒子最多只能放两个球,从而推出了错误的递推方程。


解题思路(正解):根据题意,要求方案总数,自然想到递推算法,但在设计状态函数时有不同的设法。

第一种,也是最容易想到的,求什么设什么,设f(i,j,k)表示前i个盒子最多放j个红球,k个蓝球的方案数,分析前i个盒子时,第i个盒子可以不放球,可以只放一个红球,也可以只放一个蓝球,……,最多可以放j个红球和k个蓝球,此时相对的前i-1个盒子可以最多放j个红球和k个蓝球,可以最多只放j-1个红球和k个蓝球,可以最多只放j个红球和k-1个蓝球,……,最少可以不放球(每种情况分别与前面第i个盒子放球情况相对应),则递推方程为f(i,j,k)=∑f(i-1,j-x,k-y)(0<=x<=j,0<=y<=k)。边界条件为f(0,j,k)=1,即不选盒子,最多放j个红球,k个蓝球的方案数是1(不放球)。答案即为f(n,A,B),该种算法的时间复杂度为O(n*A*B*A*B)。

第二种,是更高级且可以针对更大的数据规模的设法,因为盒子中放红球和放蓝球不相互影响,即假设你先放1个红球,之后你可以放0,1,2,...,B个蓝球,如果你放2个红球,之后你仍可以放0,1,2,...,B个蓝球,所以可以设f(i,j)表示前i个盒子最多放j个球的方案数,分析前i个盒子时,第i个盒子可以不放,可以放1个,最多放j个,此时相对的前i-1个盒子可以最多放j个,可以最多放j-1个,最少可以不放,则递推方程为f(i,j)=∑f(i-1,j-x)(0<=x<=j)。边界条件为f(0,j)=1,即不选盒子,最多放j个球只有一种方案(不放球)。因为对于每个红球的选择,都有B个蓝球的选择,所以答案为f(n,A)*f(n,B),并且在计算f(i,j)时,可以先设一个参数t记录当前f(i-1,j-x)的和,就可以省去枚举x,则此算法的时间复杂度O(n*max(A,B))。

需要注意的是,该题的答案最多为2^64-1(不是2^63-1),不能用long long,要用unsigned long long。


解法1:

#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int maxn=25;int N,A,B;/*f(i,j,k)表示前i个盒子最多放j个红球,k个蓝球的方案数f(i,j,k)=f(i-1,j,k)+f(i-1,j-1,k)+f(i-1,j,k-1)+...+f(i-1,0,0) 边界:f(0,j,k)=1*/unsigned long long d[25][20][20];void solve()  //递推算法{memset(d,0,sizeof(d));  //初始化for(int j=0;j<=A;j++)for(int k=0;k<=B;k++)d[0][j][k]=1;  //边界for(int i=1;i<=N;i++)for(int j=A;j>=0;j--)for(int k=B;k>=0;k--)for(int x=0;x<=j;x++)for(int y=0;y<=k;y++)d[i][j][k]+=d[i-1][j-x][k-y];cout<<d[N][A][B]<<'\n';}int main(){//freopen("box.in","r",stdin);//freopen("box.out","w",stdout);scanf("%d%d%d",&N,&A,&B);solve();return 0;}


解法2:

#include<cstdio>#include<cstdlib>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int maxn=25;int N,A,B;/*f(i,j)表示前i个盒子最多放j个球的方案数f(i,j)=f(i-1,j)+f(i-1,j-1)+...+f(i-1,0) 边界:f(0,j)=1*/unsigned long long d[25][20];void solve()  //递推算法{memset(d,0,sizeof(d));  //初始化for(int j=0;j<=max(A,B);j++)d[0][j]=1;  //边界for(int i=1;i<=N;i++){unsigned long long t=0;  //记录和for(int j=0;j<=max(A,B);j++){t+=d[i-1][j];d[i][j]=t;}}cout<<d[N][A]*d[N][B]<<'\n';}int main(){//freopen("box.in","r",stdin);//freopen("box.out","w",stdout);scanf("%d%d%d",&N,&A,&B);solve();return 0;}

考后反思:审题时一定要仔细啊,看错一个字就可能会丢很多分。

0 0
原创粉丝点击