赞
踩
之前gitee把外链禁用了,导致图片显示不出来,现在已经解决了
tips:以下说所的所有行和列是从0开始的,行是从左向右的,列是从上向下的!!!
首先我们需要知道的是,杨辉三角形其实就是组合数的一种形象化表示,第一行第一列代表的是 C 0 0 C_0^0 C00,而第二行第二列就是 C 1 1 C_1^1 C11……
那么我们学过组合数以后,很容易就知道第n+1行第m+1个元素(行列都是从0开始数)的值 C n m = n ! m ! ⋅ ( n − m ) ! C_n^m = \frac{n!}{m!·(n-m)!} Cnm=m!⋅(n−m)!n!
观察题目给出的元素,我们会发现杨辉三角形是一个中心对称的图形,那么我们可以猜出,题目让我们要找整数 N N N第一次出现的位置,那么我们可以确信的是, N N N第一次出现一定是在整个三角形的左半部分,所以我们可以把整个图形切成一半进行分析
如下:
我们通过观察可以发现,对于第 i i i行,其最大值就在第 [ i 2 ] [\frac{i}{2}] [2i]列(这里地行和列都是从0开始数的)
然后我们手动拿计算器或者写程序枚举一下,发现 C 32 16 C_{32}^{16} C3216是小于 1 0 9 10^9 109的,而 C 34 17 C_{34}^{17} C3417是大于 1 0 9 10^9 109的,所以我们可以从第16列开始往前枚举。(why:其实对于第 i i i行,第 [ i 2 ] [\frac{i}{2}] [2i]列( C i [ i 2 ] C_i^{[\frac{i}{2}]} Ci[2i])确实是第 i i i行的最大值,但是对于第 [ i 2 ] [\frac{i}{2}] [2i]列,第 i i i行( C i [ i 2 ] C_i^{[\frac{i}{2}]} Ci[2i])其实是这一列的最小值,这个从上面的图应该可以看出来)
这里先说一下,在对半分的杨辉三角形中,一行的数据是从左向右递增的,一列的数据是从上至下递增的,我们想要找到一个数 N N N的话,这个数第一次出现一定是在靠近右边列的地方,因为我们从最右边的列开始找,而最右边的列一定是这一列每个数所在行的最大值,如果找到了 N N N,那么前面行的所有数一定不会大于 N N N,那么他就是出现的第一个 N N N了
所以这就是为什么要从右往左,从上向下查找的原因了。
我们发现列数并不多,但是行数很多,因为最坏的情况下,我们找到的第一个 N N N就是在第N行的第1列( C N 1 C_N^1 CN1),所以对于每一列,我们要从这一列的第一个元素 C i [ i 2 ] C_i^{[\frac{i}{2}]} Ci[2i]搜索到 C N [ i 2 ] C^{[\frac{i}{2}]}_N CN[2i],但是我们知道对于每一列的元素都是递增的,所以在进行列搜索的时候我们可以使用二分法来进行搜索
package com.lanqiao; import java.util.Scanner; /** * @author 王宇哲 * @date 2022/3/19 16:10 */ public class 杨辉三角形 { static int N; /** * 求组合数<br/> * C_n^m = [n*(n-1)*...*(n-m+1)] / [1 * 2 * ... * m] * @param m 选几个球? * @param n 从几个球中选? * @return 选择方案数 */ static long C(int m, int n){ long res = 1; for(int i = n, j=1; j<=m;j++,i--){ res *= i; res /= j; //如过算出来已经大于N了,继续算不仅没意义,而且会越界,出现负数,扰乱判断 if(res > N){ return res; } } return res; } /** * 在这一列中搜索N第一次出现的位置 * @param column 当前搜索的列数 * @return N第一次出现的位置,没出现就返回-1 */ static long searchFirstAppearByColumn(int column){ // 这一列的第一个元素是在2column处 int left = 2 * column; //搜索到第N行就可以停止了 //这里注意一下,如果搜索1,那么在第1列的时候,左边界是2,N是1 //如果右边界是N的话最后搜索到的就是第1行第1列的1,那么输出结果就会变成3,这明显是不成立的 //所以我们要保证开始搜索时左边界要小于等于右边界 int right = Math.max(N,left); int mid = (left + right) >> 1; //暂存计算结果 long tmpResult; //二分搜索 while(left <= right){ //位运算,相当于浅浅加个速,但是加了不少逼格 mid = (left + right) >> 1; tmpResult = C(column,mid); //找到了就退出循环 if(tmpResult == N){ break; } if(tmpResult > N){ //如果找的数过大了,就从左区间继续找,为了避免死循环,右边界要减1 right = mid-1; }else{ //为了避免死循环,左边界要加1 left = mid+1; } } //如果找到了的值确实是N if(C(column, mid) == N){ //right是行号 return (long) (mid) *(mid +1)/2 + column + 1; } return -1; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); N = scanner.nextInt(); long ans; for(int i = 16 ;i >= 0;i--){ //赋值完来个比较,不是-1就是找到了,返回其位置 if((ans = searchFirstAppearByColumn(i)) != -1){ System.out.println(ans); break; } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。