zoukankan      html  css  js  c++  java
  • 经典基础算法之面试题(系列一)(转)

    1. 打靶问题的递归解法

    伦敦奥运会火热进行中,让我们来看个打靶的问题:一个射击运动员打靶,靶一共有10环,求连开10枪打中90环的可能行有多少种?
    分析:这是一个典型递归求解问题。假设第10枪打x环,则将问题转换为剩下9枪打90-x环的可能有多少种,x的取值范围为[0, 10],根据加法原理,则:10枪打90环的可能 = 第10枪打0环,剩下9枪打90环的可能 + 第10枪打1环,剩下9枪打89环的可能 + 第10枪打2环,剩下9枪打88环的可能 
    + 第10枪打3环,剩下9枪打87环的可能 + 第10枪打4环,剩下9枪打86环的可能 + 第10枪打5环,剩下9枪打85环的可能 
    + 第10枪打6环,剩下9枪打84环的可能 + 第10枪打7环,剩下9枪打83环的可能 + 第10枪打8环,剩下9枪打82环的可能
    + 第10枪打9环,剩下9枪打81环的可能 + 第10枪打10环,剩下9枪打80环的可能。

                                                                             image

    递归的停止条件为:
    1. 若环数小于0 或者 剩下的环数大于剩下的枪数乘以10(即剩下每枪打10环用不玩所剩环数),则该递归路径不记入可能情况。
    2. 若不满足条件1,且所剩枪数为1,则该递归路径记为1中可能情况。

     1:  #include <stdio.h>
     2:  
     3:  int recursion(int count, int score)
     4:  {
     5:      if (score < 0 || score > count * 10) {
     6:          return 0;
     7:      }
     8:   
     9:      if (1 ==  count) {
    10:          return 1;
    11:      }    
    12:   
    13:      int i = 0;
    14:      int sum = 0;
    15:   
    16:      for (; i <= 10; ++i) {
    17:          sum += recursion(count - 1, score - i);
    18:      }
    19:   
    20:      return sum;
    21:  }
    22:   
    23:  int main()
    24:  {
    25:      printf("result: %d\n", recursion(10, 90));        
    26:      return 0;
    27:  }

    2. 求多项式的值与求幂的快速算法

    2.1 求多项式的值

    已知多项式中的系数a0,a1,a2……an及x的值,求f(x)。
    image
    多项式求值问题可以利用辗转相乘的方式进行计算,根据上面的式子分析,可以将上式转换为: f(x) = ((((an * x) + an-1) * x + an-2) * x  ……. + a1) * x + a0 
    有了这个变形,代码自然而然就能写出来了:

     1:  #include <stdio.h>
     2:  
     3:  int polynomial(const int *coefficient, int n, int x)
     4:  {
     5:      int sum = 0;
     6:   
     7:      int i = n - 1;
     8:      for (; i != 0; --i) {
     9:          sum = sum * x + coefficient[i];
    10:      }
    11:   
    12:      return sum * x + coefficient[0];
    13:  }
    14:   
    15:  int main()
    16:  {
    17:      int coeff[5] = {1, 2, 3, 4, 5};
    18:      printf("result: %d\n", polynomial(coeff, sizeof(coeff) / sizeof(coeff[0]), 10));
    19:      return 0;
    20:  }

    2.2 求幂的快速算法

    求x的p次幂本是简单的问题,可以将x次乘p次就可以了,这里我们当然不是要讨论这种计算方法,这里讨论的是怎样高效计算。
    有没有可能减少做乘法的次数呢?让我们来做一个分析,考虑x的6次幂的情况:x * x * x * x * x * x = (x * x * x) * (x * x * x)
    等式前面部分需进行6次乘法,后半部分需计算(x * x * x) ,然后乘以上次计算的值即可,共4次乘法。
    通过上述分析可知,求x的p次幂可以通过递归折半的方法来减少乘法的次数,循着这个思考,实现就不太困难了。

     1:  #include <stdio.h>
     2:  
     3:  /* 统计乘法的次数 */
     4:  static int multiply_count = 0;
     5:   
     6:  /* 求x的p次幂 */
     7:  int power(int x, int p)
     8:  {
     9:      int ret =  0;
    10:   
    11:      if (1 == p) {
    12:          return x;
    13:      }
    14:   
    15:      /* 先计算x的p/2次幂 */
    16:  ret = power(x, p >> 1);
    17:      ret *= ret;
    18:      ++multiply_count;
    19:   
    20:      /* 若p是奇数,则再乘一次x */
    21:  if (0 != p % 2 /* 求模本身是一个耗性能的运算,这里可优化为 p – ((p >> 1) << 1) */) { 
    22:          ret *= x;
    23:          ++multiply_count;
    24:      }    
    25:   
    26:      return ret;
    27:  }
    28:   
    29:  int main()
    30:  {
    31:      printf("resulut: %d\n", power(2, 20));    
    32:      printf("multiply count: %d\n", multiply_count);
    33:      return 0;
    34:  }

    计算2的20次幂总共进行5次乘法就足够了,而原始的算法需要20次,那么这种算法究竟需要多少次乘法呢?
    由于该算法根本思想是折半递归,类似于2分查找,所以乘法的次数为lgp取天花板值这个数量级的(lgp表示以2为底p的对数)。
    整型int最多表示2的32次幂,因此最多节省24次乘法,对于现代的计算机,这似乎不是特别重要,但是在以下情况下该算法具有重要价值:
    1. 对于需要高频率计算幂的情况;
    2. 对于大数高精度计算的情况,如需计算2的10000次幂。
    当然了,这也是对递归算法和2分法的巧妙应用,学习其思想吧。

    3. 一年中的第n天是几月几号?

    和这个问题类似的问题还有:
    1. 给定某年某月某日,问这是这年的第多少天?
    2. 已知某年的1月1号是星期几,求给定的某年某月某日是星期几?

    其实这类问题的共同点在于它们都需要考虑闰年问题,大月小月问题;
    首先我们解决闰年问题,根据闰年的定义定义如下宏来判断某年是否是闰年:
    #define IS_LEAP(X) (((X) % 400 == 0 || (X) % 100 != 0 && (X) % 4 == 0) ? 1 : 0)

    在来看大月小月问题,可以定义如下二维数组,用day_count_of_month[0][12]表示润年情况下每月的天数,用day_count_of_month[1][12]表示非润年情况下每月的天数(这就是传说中的字典法了,就是根据提供的信息查表,类似于查字典,所以叫做字典法):
    int day_count_of_month[2][12] = {

                                                    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
                                                    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}

                                                   };

    1:  #include <stdio.h> 
     2:  
     3:  #define IS_LEAP(X) (((X) % 400 == 0 || (X) % 100 != 0 && (X) % 4 == 0) ? 1 : 0)
     4:  int day_count_of_month[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
     5:                                   {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; 
     6:  /* 计算year年的第n天是几月几号 */
     7:  void convert_to_date(int year, int n) 
     8:  { 
     9:      int leap = IS_LEAP(year); 
    10:      if(year < 1970 || year > 5000 || n <= 0 || n > 365 + leap) { 
    11:          printf("bad year\n"); 
    12:      }
    13:   
    14:      int i = 0; 
    15:      for(; n > day_count_of_month[leap][i]; ++i) { 
    16:          n -= day_count_of_month[leap][i]; 
    17:      } 
    18:   
    19:      printf("year : %d, month : %d, day : %d\n", year, i + 1, n); 
    20:  } 
    21:   
    22:  int main() 
    23:  { 
    24:      convert_to_date(2012, 230); 
    25:  } 

    上面这段代码解决了第一个问题,第二个问题可以转换为类似第一个问题的问题:
    先计算给定的某年某月某日到给定已知这天的总天数s=>s%7=>利用模运算结果来推算所求日期的星期情况即可。

     

    http://www.cnblogs.com/dskit/archive/2012/08/11/2633736.html

     

  • 相关阅读:
    51nod数字1的数量
    bzoj3669: [Noi2014]魔法森林 lct版
    【NOI2014】起床困难综合症 位运算+贪心
    bzoj2631: tree lct
    bzoj2002 弹飞绵羊 lct版
    codevs1245最小的N个和 小根堆
    RTSP/GB28181/SDK协议视频融合平台EasyCVR接口获取协议平台接入参数的调用方法
    RTSP/GB28181/SDK协议视频融合平台EasyCVR上传通道数据库不显示怎么解决?
    基于视频协议融合平台EasyCVR开发的视频综合管理监控平台EasyCVS通道流检查功能的实现
    RTSP/GB28181/HIKSDK/大华SDK协议安防视频云平台EasyCVR新增告警功能介绍
  • 原文地址:https://www.cnblogs.com/bizhu/p/2646325.html
Copyright © 2011-2022 走看看