问题 1191: 化学品问题【借鉴】【详细解析】【哭了】

时间限制: 1Sec 内存限制: 128MB

题目描述

一个实验室有N个放化学品的试管,排列在一条直线上。如果连续M个试管中放入药品,则会发生爆炸,于是,在某些试管中可能不放药品。
任务:对于给定的N和M,求不发生爆炸的放置药品的方案总数

输入
第一行是一个正整数L,代表输入数据的组数
接下来L行,每行有两个正整数N,M( 1<N<32,2≤M≤5)
输出
输出L行,每行只有一个正整数S,表示对应输入数据的方案总数。

样例输入
2
4 3
3 2
样例输出
13
5

Code


Code 1 错误50%

错误原因,多种情况少算
如下面情况

n=6		m=3
位置	1	2	3	4	5	6
摆放	1	1		1	1
#include<stdio.h>
//阶乘器
int Factorial(int n)
{
    if(n<=1)    return 1;
    int result=1,i;
    for(i=2;i<=n;i++)
        result*=i;
    return result;
}
//数学函数C
int FC(int n,int m)
{
    if(!m)  return 1;
    int i,result=1;
    for(i=n;i>n-m;i--)
        result*=i;
    return result/Factorial(m);
}
int main()
{
    int L,m,n,i,count=0;
    scanf("%d", &L);
    while(L--){
        scanf("%d%d", &n,&m);
        //注意此处的count初始化
        for(i=0,count=0;i<=m&&i<=n;i++){
            if(i==m)    count+=FC(n,i)-FC(n-i+1,1);
            else    count += FC(n, i);
        }
        printf("%d\n", count);
    }
    return 0;    
}

Code 2【借鉴】

n=0 m=3     0
n=1 m=3     0   1       /2
n=2 m=3     0   1   2   12      /4
n=3 m=3     0   1   2   3   12  13  23      /7      123
n=4 m=3     0   1   2   3   4   12  13  14  23  24  34  124 134     /13     234
n=5 m=3     0   1   2   3   4   5   12  13  14  15  23  24  25  34  35  45  124 125 134 135 145 235 245 1245    /24     345 1345
可以看出
第n=3比n=2多出一个123
第n=4比n=3多出一个234
第n=4比n=4多出一个345和1345

分为三种情况:
第一种:n<m
此时可以随便放,每个试管共有2种,所以共有2n情况
第二种:n=m
此时爆炸的情况只有一种,即全部试管都放药剂。
总情况有2n-1
第三种:n>m
思考方式为:在n-1个试管的基础上再在最后放一个试管
1.最后一个试管不放药剂,前n-1个试管的摆放情况通过递归可以求得ia[n-1][m]
2.最后一个试管放药剂,此时需要排除会爆照的情况,即n,n-1,n-2...n-m+1(共m-1个试管)都放药的情况
为什么是m-1个试管?
因为在n-1的基础上是都不会爆照的,所以在最后增加一个有药的试管时,爆炸的原因只可能是前面有m-1个有药的试管
前面有一个有药的试管和后面m-1个试管中间肯定有没药的将其分开。
关键是怎么求这种会爆炸的情况?
首先是以n-1的情况为基础的,然后将最后m-1个去掉,此时共有n-m个试管,因为中间至少必有一个试管将前面一个有药剂的试管和后面m-1个有药剂的试管分开,而且这个试管一定和后面的m-1个试管连在一起的。
根据最后m个试管的情况,就确定了前面试管一定不会爆炸。
然后根据递归函数的功能:就可以得出前面n-m-1个试管不会爆照的放药剂的情况ia[n-m-1][m]
所以第三种情况的总情况数为:2*ia[n-1][m]-ia[n-m-1][m]
注:如果n-m-1=0的情况下,返回的是1

#include<stdio.h>
#include<string.h>
#include<math.h>
//进行排列
int Arrange(int ia[][6],int n,int m)
{
    if(ia[m][n])    return ia[m][n];//记忆数组
    if(m==n){
        ia[m][n]=(int)pow(2,n)-1;//除去全部都有试剂的情况
        return ia[m][n];
    }
    if(n<m){
        ia[m][n]=(int)pow(2,n);
        return ia[m][n];
    }
    //n>m
    //在n-1个试管的基础上再在最后放一个试管
    //1.最后一个试管不放药剂,前n-1个试管的摆放情况通过递归可以求得ia[n-1][m]
    //2.最后一个试管放药剂,此时需要排除会爆照的情况,即n,n-1,n-2...n-m+1(共m-1个试管)都放药的情况
    return Arrange(ia,n-1,m)+ Arrange(ia, n-1, m)- Arrange(ia, n-m-1, m);
}
int main()
{
    int L,ia[33][6],n,m;//int array-->ia
    scanf("%d", &L);
    while(L--){
        memset(ia,0,sizeof(ia));//initialize
        scanf("%d%d", &n,&m);
        printf("%d\n",Arrange(ia,n,m));
    }
    return 0;
}
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值