• POJ1942Paths on a Grid


    转载请注明出处:優YoU  http://user.qzone.qq.com/289065406/blog/1301543725

    大致题意:

    给定一个矩形网格的长m和高n,其中mn都是unsigned int32类型,一格代表一个单位,就是一步,求从左下角到右上角有多少种走法,每步只能向上或者向右走

     

    解题思路:

    非常水的中学数学题,用组合做

    先简单建立一个数学模型:

    只要给定了长m和高n,那么要从左下角走到右上角,不管怎么走,一定要往右走m次,往上走n

    例如给定 m=5,n=4

    那么可以  上上上上上右右右右

    又可以    上右上右上右上右上

    等等。。。

    关键是“上”和“右”的先后问题,就是组合问题了

    那么数学模型就是

    n+m个位置,选择n个位放“上” (那么剩下m个位一定是“右”)


     

    处理阶乘有三种办法:

    (1)       传统意义上的直接递归,n的规模最多到20+,太小了,在本题不适用,而且非常慢

    (2)       稍快一点的算法,就是利用log()化乘为加,n的规模虽然扩展到1000+,但是由于要用三重循环,一旦n规模变得更大,耗时就会非常之严重,时间复杂度达到O(n*m*(n-m)),本题规定了nmunsigned int32类型,就是说n,m的规模达到了21E以上,铁定TLE的。而且就算抛开时间不算,还存在一个致命的问题,就是精度损失随着n的增加会变得非常严重。

    因为n有多大,就要进行n次对数运算,n规模一旦过大,就会丢失得非常严重了。所以这种方法是绝对不可取的,因为中途的精度丢失不是简单的四舍五入可以挽回的。

    (3)       拆分阶乘,逐项相除,再乘以前面所有项之积。这种方法用一个循环就OK了,时间复杂度只有O(n-m),非常可观。

     

     下面我根据程序详细说说算法(3):

           double cnm=1.0;

           while(b>0)

                  cnm*=(double)(a- -)/(double)(b- -);

     

    这是我写的函数原型,计算的是 aCb

     

    这种算法巧妙地利用了分子分母的关系,而不是把公示中的3个阶乘单独处理。

    例如当 a=5,b=2


     

    由于用了 double去计算组合数,那么最后要转化为 无符号整型 时就要处理精度问题,有两种方法:四舍五入+强制类型转换  或者 setprecision()函数

    详细看我的两个程序

     1 /*强制类型转换输出*/
    2
    3 //Memory Time
    4 //220K 0MS
    5
    6 #include<iostream>
    7 #include<math.h>
    8 using namespace std;
    9
    10 /*Compute (n+m)C min{n,m}*/
    11
    12 unsigned comp(unsigned n,unsigned m)
    13 {
    14 unsigned a=m+n;
    15 unsigned b=(m<n?m:n);
    16 double cnm=1.0;
    17 while(b>0)
    18 cnm*=(double)(a--)/(double)(b--);
    19
    20 cnm+=0.5; //double转unsigned会强制截断小数,必须先四舍五入
    21 return (unsigned)cnm;
    22 }
    23
    24 int main(void)
    25 {
    26 unsigned m,n;
    27 while(true)
    28 {
    29 cin>>m>>n;
    30 if(!m && !n)//承认这题的猥琐吧!竟然有其中一边为0的矩阵,一定要&&,用||会WA
    31 break;
    32
    33 cout<<comp(n,m)<<endl;
    34 }
    35 return 0;
    36 }

    =============华丽的分割线================

     1 /*自定义精度输出*/
    2
    3 //Memory Time
    4 //220K 0MS
    5
    6 #include<iostream>
    7 #include<math.h>
    8 #include<iomanip>
    9 using namespace std;
    10
    11 /*Compute (n+m)C min{n,m}*/
    12
    13 double comp(unsigned n,unsigned m)
    14 {
    15 unsigned a=m+n;
    16 unsigned b=(m<n?m:n);
    17 double cnm=1.0;
    18 while(b>0)
    19 cnm*=(double)(a--)/(double)(b--);
    20
    21 return cnm;
    22 }
    23
    24 int main(void)
    25 {
    26 unsigned m,n;
    27 while(true)
    28 {
    29 cin>>m>>n;
    30 if(!m && !n)
    31 break;
    32
    33 cout<<fixed<<setprecision(0)<<comp(n,m)<<endl;
    34 //fixed是为了固定小数位数
    35 //setprecision()函数是会自动四舍五入的,所以不用像强制类型转换那样预先+0.5
    36 }
    37 return 0;
    38 }
  • 相关阅读:
    Python 规范
    Hql
    Python
    IIS 日志分析
    NHibernate 知识点整理
    微软开放了.NET 4.5.1的源代码
    自定义消息编码绑定实现
    使用自定义绑定
    WCF安全:通过 扩展实现用户名密码认证
    WCF 几种错误
  • 原文地址:https://www.cnblogs.com/lyy289065406/p/2122781.html
Copyright © 2020-2023  润新知