杭电acm1085 Holding Bin-Laden Captive!

杭电acm1085

                             Holding Bin-Laden Captive!

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9057    Accepted Submission(s): 4078


Problem Description
We all know that Bin-Laden is a notorious terrorist, and he has disappeared for a long time. But recently, it is reported that he hides in Hang Zhou of China! 
“Oh, God! How terrible! ”



Don’t be so afraid, guys. Although he hides in a cave of Hang Zhou, he dares not to go out. Laden is so bored recent years that he fling himself into some math problems, and he said that if anyone can solve his problem, he will give himself up! 
Ha-ha! Obviously, Laden is too proud of his intelligence! But, what is his problem?
“Given some Chinese Coins (硬币) (three kinds-- 1, 2, 5), and their number is num_1, num_2 and num_5 respectively, please output the minimum value that you cannot pay with given coins.”
You, super ACMer, should solve the problem easily, and don’t forget to take $25000000 from Bush!
 


Input
Input contains multiple test cases. Each test case contains 3 positive integers num_1, num_2 and num_5 (0<=num_i<=1000). A test case containing 0 0 0 terminates the input and this test case is not to be processed.
 


Output
Output the minimum positive value that one cannot pay with given coins, one line for one case.
 


Sample Input
  
  
1 1 3
0 0 0
 


Sample Output
  
  
4
 


Author
lcy
 


 

我原来的代码:没看清问题要求,WA了,题目主要是要用户分别输入1,2,5三种硬币的数量,然后凑面值,第一个凑不出的面值,例如:

1,2,5数量分别为1,1,3,时,就是4凑不出来,数量分别为:2,2,2,第一个凑不出就是17;

错误实现代码:

#include <iostream>
using namespace std;

int main()
{
 int num[3],i,j;
 int sum=0,count=0;
 while(1)
 {
  for(i=0;i<3;i++)
  {
   sum =0,count =0;
   cin>>num[i];
   if(num[i]<=0 || num[i]>=1000)
    return 0;
  }
  if(num[1] == 0 && num[2] == 0 && num[0] == 0)
   return 0;
  for(j=0;j<3;j++)
  {
     if(num[j]>=5)
     {
      count = num[j]/5;
      if(num[j] % 5 == 0)
       sum += count;
      else if(num[j]%5 == 1 || num[j]%5 == 2)
       sum = sum + count + 1;
      else
       sum = sum + count +2;
                   
     }
     else if(num[j]<3 && num[j]>0)
      sum += 1;
     else if(num[j]>=3 && num[j]<5)
      sum += 2;
     else 
      sum += 0;
  }
    cout<<sum<<endl;
 }
 return 0;
}

 

 摘自网上:

母函数:

什么是母函数?对于序列a0,a1,a2,…构造一函数:G(x)=a0+a1*x+a2*x^2+...G(x)就是序列a0,a1...的母函数。如若已知序列a0,a1,a2,…则对应的母函数G(x)便可根据定义给出。 反之,如若已经求得序列的母函数G(x),则该序列也随之确定。 序列a0,a1,a2,…可记为{an} 

如何用母函数来解决诸如整数拆分、邮票组合、砝码称重一类的问题?这一类问题大致是需要找到整数拆分的方案数,邮票可以组合出多少面额,某一种重量可以由多少种砝码组合来完成称重……当然利用类似动态规划的思想往往也可以解决这类问题,下面讨论怎么使用母函数来完成。

下面这个例子来自HDU集训的PPT

“例1:若有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?各有几种可能方案? 
如何解决这个问题呢?考虑构造母函数。如果用x的指数表示称出的重量,则:
  1个1克的砝码可以用函数1+x^1表示,1个2克的砝码可以用函数1+x^2表示,
  1个3克的砝码可以用函数1+x^3表示,1个4克的砝码可以用函数1+x^4表示,
几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:
(1+x)(1+x^2)(1+x^3)(1+x^4)
=(1+x+x^2+x^3)(1+x3+x^4+x^7)
=1+x+x^2+2x^3+2x^4+2x^5+2x^6+2x^7+x^8+x^9+x^10  
从上面的函数知道:可称出从1克到10克,系数便是方案数。

理解了这个例子之后整数拆分、邮票组合、砝码称重一类的问题就都一并解决了。

下面来是我对这种母函数构造方式的理解。

对于上面的例1,如果取消砝码个数的限制,则母函数变为G(x)=(1+x+x^2+,,,,,)(1+x^2+x^4+...)(1+x^3+x^6+...)(1+x^4+x^8+..)

若要称量重量M,那么这个质量M就对应母函数展开后的x^M项。而x^M项的指数M按照数学上的展开来理解是什么来的呢?

假设M=14,我们用1个1g砝码,1个2g砝码,1个3g砝码和2个4g砝码。则可以看做是从G(x)=(1+x+x^2+,,,,,)(1+x^2+x^4+...)(1+x^3+x^6+...)(1+x^4+x^8+..)的每一个括号(每个括号分别对应1g, 2g, 3g, 4g砝码单独可以组合出哪些质量)里取出了x, x^2, x^3, x^8。然后意会一下为什么x^M项对应的系数就是称量质量M的方案数了。如果理解的话这一类问题就都可以做了。

实现起来,展开G(x)函数的时候就是按照平时自己手工怎么展开来做的。


 

硬币面额1,2,5且有数量限制num1,num2,num3,问最小不能组合的数量是多少。

G(x)=(1+x+...+x^num1)(1+x^2+...+x^2num2)(1+x^5+,,,+x^5num3),展开,系数不为0的数都是可以由硬币组合出来的。

 

正确实现代码:

#include <iostream>
using namespace std;
 

//c[i]表示x的i次方的系数 ,例如c1[5]=3,即表示x^5的系数,系数代表方案数,系数不为0,则表示能组合出来的面值
int c1[10000], c2[10000];
int num[4];
int main()
{
    int nNum;
    while(scanf("%d %d %d", &num[1], &num[2], &num[3]) && (num[1]||num[2]||num[3]))
    {
        int _max = num[1]*1+num[2]*2+num[3]*5;
        // 初始化
        for(int i=0; i<=_max; ++i)
        {
            c1[i] = 0;
            c2[i] = 0;
        }
        for(int q=0; q<=num[1]; ++q)    //1+x+x^2+...+x^n1   
            c1[q] = 1;                 //开始时系数全为一   
        for(int w=0; w<=num[1]; ++w)    //第一个括号内的数与第二个括号相乘
            for(int j=0; j<=num[2]*2; j+=2)         //1+x^2+...+x^(2*n2)
                c2[j+w] += c1[w];           //合并同类项   如:(4*x^3) *(2*x^3)  = 6*x^3     即:c1[3] = 4   ,c2[3]=2  ,则相乘的话,等于c2[3] = 6  
        for(int r=0; r<=num[2]*2+num[1]*1; ++r)       // 看到范围的变化了吗?
        {
            c1[r] = c2[r];   //把暂时的存在要输出的c1数组上
            c2[r] = 0;
        }
 

        //一二括号的总值与第三个括号相乘
        for(int t=0; t<=num[1]*1+num[2]*2; ++t) //此时x的幂最大是n1+2*n2   
            for(int y=0; y<=num[3]*5; y+=5) //1+x^5+...+x^(5*n3) 
                c2[y+t] += c1[t];
        for(int z=0; z<=num[2]*2+num[1]*1+num[3]*5; ++z)  //n1+2*n2+5*n3是此时x的最大幂
        {
            c1[z] = c2[z];
            c2[z] = 0;
        }
        int b;
 
        for(b=0; b<=_max; ++b)
            if(c1[b] == 0)//为0,表示不能组合出来的项,输出
            {
                printf("%d\n", b);
                break;
            }
        if(b == _max+1)
            printf("%d\n", b);
    }
    return 0;

 

 

参考资料:

http://www.wutianqi.com/?p=596

http://blog.sina.com.cn/s/blog_79b832820100vc2d.html

 

找出规律,更简单的方法:

#include <stdio.h>

int main()

{

       int num_1, num_2, num_3;

       while (scanf ("%d%d%d", &num_1, &num_2, &num_3), num_1+num_2+num_3)

       {

              if (num_1 == 0)  printf ("1\n");

              else if (2*num_2+num_1 < 4)   printf ("%d\n",2*num_2+num_1+1);

              else  printf ("%d\n",5*num_3+2*num_2+num_1+1);

       }

       return 0;

}

 

 摘自网上,觉得蛮好的:

我们举一个例子说明,一些具有实际意义的组合问题也可以用像这样简单的一个函数全部表示出来。 
考虑这个问题:从二班选n个MM出来有多少种选法。学过简单的排列与组合的同学都知道,答案就是C(4,n)。也就是说。从n=0开始,问题的答案分别是1,4,6,4,1,0,0,0,...(从4个MM中选出4个以上的人来方案数当然为0喽)。那么它的生成函数g(x)就应该是g(x)=1+4x+6x^2+4x^3+x^4。这不就是……二项式展开吗?于是,g(x)=(1+x)^4。 
你或许应该知道,(1+x)^k=C(k,0)x^0+C(k,1)x^1+...+C(k,k)x^k;但你或许不知道,即使k为负数和小数的时候,也有类似的结论:(1+x)^k=C(k,0)x^0+C(k,1)x^1+...+C(k,k)x^k+C(k,k+1)x^(k+1)+C(k,k+2)x^(k+2)+...(一直加到无穷;式子看着很别扭,自己写到草稿纸上吧,毕竟这里输入数学式子很麻烦)。其中,广义的组合数C(k,i)就等于k(k-1)(k-2)(k-i+1)/i!,比如C(4,6)=4*3*2*1*0*(-1)/6!=0,再比如C(-1.4,2)=(-1.4)*(-2.4)/2!=1.68。后面这个就叫做牛顿二项式定理。当k为整数时,所有i&gt;k时的C(k,i)中分子都要“越过”0这一项,因此后面C(k,k+1),C(k,k+2)之类的都为0了,与我们的经典二项式定理结论相同;不同的是,牛顿二项式定理中的指数k可以是任意实数。 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值