题目描述:给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。

 

 

 

 

 

 

 

(题目及图片复制于Leetcode


思考:这图一看脑子大概就有了个解决过程。从杨辉三角的生成规律可以知道,第n+1行的结果是从第n行产生的,所以很容易就能想到动态规划的解法。

动态方程:dp[i][j] = dp[i-1][j-1]+dp[i-1][j];

(但是具体的操作还有更加妙的处理方式,主要得益于发现者敏锐地观察力)

不过作为一名高中生,被组合数统治的恐惧依旧未散,一看到”1,3,3,1“”1,4,6,4,1“脑子里就蹦出了$C_{3}^{k}和C_{4}^{k}\dots$

下面开始实现:

 


方法零:打表(oier都懂得~)

方法一:动态规划

思路:一个是刚开始就构建一个包含所有前numRows行的数组数组,然后循环每个数组进行更新。

但是这样浪费空间,实际上每次操作只需要两个数组,一个是上一行,一个是本行。

然后再思考思考,是不是一行也可以呢?

可以!

这里就提到一个发现的规律(当然,是可以证明的):

比如上一行是     1        3         3            1

下一行就是     1      4         6           4            1

这两行有什么关系呢?

看:    1         3           3             1         0

+         0         1           3             3         1           

_________________________________

=>       1         4           6             4          1               (数组向右偏移一个,然后对应数字相加)

于是一个数组就可以完美解决。(具体细节自己阅读代码哦)

(注意: 由于数组大小变动,用vector数据结构比较方便)

<c++>

 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 void showcase(vector <int> &outp);//显示一行
 5 int main()
 6 {
 7     int numRows;
 8     vector <int>outp(1, 1);//期初数组是{1}
 9 
10     cout << "请输入行数:";
11     cin >> numRows;
12 
13     showcase(outp); // 先把第一行输出了
14     for (int i = 1; i < numRows; ++i)
15     {
16         outp.push_back(1);               //最后一个一定是1了
17         for (auto elm = outp.end()-2; elm != outp.begin(); elm--)//大细节!若从前往后就会出问题,建议自己尝试
18         {                              //从第二项处理到倒数第二项  因为两边的都已经是1了
19             *elm = *(elm - 1) + *(elm);//此时前一项相当于上一行的对应数,
20         }                              //本项相当于上一行偏移后的对应数
21         showcase(outp);
22     }
23     system("pause");
24     return 0;
25 }
26 void showcase(vector<int> &outp)//显示本行
27 {
28     int siz = outp.size();
29     for (int i = 0; i < siz; ++i)
30     {
31         cout << outp[i] << "  ";
32     }
33     cout << endl;
34 }

 

方法二:构造组合数函数

思路:构造组合数函数
杨辉三角第n行个数分别为$C_{n-1}^{0},C_{n-1}^{1},\cdots ,C_{n-1}^{n-1} n\geq 2$  

所以我们只需要构造一个Combine组合数函数(组合数不懂得可以百度哦网上教程可比我讲得全面)

遇到的问题及解决办法:
1.如果单单构建阶乘函数,那么等numRows够大后 unsigned long long都不够
解决办法:于是就直接构建了组合数函数(比如$C_{7}^{2}$只是7*6/2,阶乘的话就得算出7!)
2.但是构造阶乘函数unsigned long long还是不够用
解决办法:函数内部计算时用double 最后再转换成int;

 代码:

<c++>

 

 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 int Combine(double a, double b);// 求组合数的函数
 5 int main()
 6 {
 7     int numRows;
 8     cout << "请输入行数:";
 9     cin >> numRows;
10     // ans[0][0]=1;                     
11     if (numRows == 1){ cout << 1 << endl; system("pause"); return 0; }//特判
12     for (int a = 0; a<numRows; ++a)     //numRows >=2
13     {
14         for (int b = 0; b <= a; ++b)
15         {
16             cout << Combine(a, b) << "  ";
17         }
18         cout << endl;
19     }    
20     system("pause");
21     return 0;
22 }
23 /*unsigned long long Factorial(int n)      //阶乘   but it turned out to be a failure
24 {
25     if(n == 0)return 1;
26     n++;
27     unsigned long long  ans=1;
28     for (int i = 1;i<n;++i)
29     {
30         ans *= i;
31     }
32     return ans;
33 }*/
34 int Combine(double a, double b)//组合数函数
35 {
36     double ans = 1;
37     if (b == 0 || a == b)return 1;                     //组合数定义
38     int terminal = b>(a - b) ? b : (a - b);              //多算算组合数题目就懂了
39     for (double i = 1; a>terminal; ans *= a / i, --a, ++i);
40     return int(ans + 0.5);            //小细节
41 }

 

 


总结:使用组合数的优势似乎并不是很明显(而且有点吃数学知识)。不过这个思想却是从这个例子中最大的收获。很多算法问题,顺水推舟去解决往往总会止步在效率的上限,但是用上数学的思想和工具就会有意想不到的效果(有时对效率的改进是颠覆性的)。