蓝桥杯——杨辉三角分析总结

这篇博客探讨了如何高效地在杨辉三角形中查找特定数值首次出现的位置。作者指出,使用传统的存储和遍历方法在处理大规模数据时会遇到时间和空间复杂度的问题。为了解决这个问题,提出了利用递推关系只维护前两行的方法,并通过观察发现当第三列数值超过目标数值时,可以提前终止递推并用数学公式计算出正确位置。博客还详细解释了代码实现过程,并强调了数据规模预估和边界条件处理的重要性。

题目描述

下面的图形是著名的杨辉三角形:

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列: 1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,⋯

给定一个正整数 N,请你输出数列中第一次出现 N是在第几个数?

输入描述

输入一个整数 NNN。

输出描述

输出一个整数代表答案。

输入输出样例

示例:输入6,输出13

评测用例规模与约定

对于 20% 的评测用例,1≤N≤10​; 对于所有评测用例,1≤N≤1000000000

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

分析总结:获取到杨辉三角,在此基础上查找某个数第一次出现的位置,最初的想法是用vector<vector<long long>>来保存一张很大的杨辉三角,但对于最大测试用例1000000000来说,运算的时间和保存的空间不可接受,且遍历整张表查找元素确实有点可爱。所以从节约空间出发,用一个二维数组只维护上一行和这一行,以递推的形式逐渐向后搜索,与此同时判断当前元素是否是要查找的元素,在递推杨辉三角的过程中可以发现每一行都有对称的情况,所以只需递推出每行的一半即可,

但即使是这样也无法解决n取100000000时的情况,通过观察发现当n比较大时,例如n取14,在递推查找的过程中,当当前行第三列的数大于14时,那么14就只能出现在递增的第二列,因为当前行第三列元素向右和向下都是增加的,此时就可以跳出递推过程,通过数学方法直接计算出n的位子,对于n取14的情况数学公式是(n+1)*(n/2)+(n%2)*(n/2+1)。

编写代码时实际考虑的关系是下图

最终的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<vector>
typedef long long LL;
using namespace std;
LL a[2][23000];//a[0]用来维护第三行,a[1]用来维护第四行,此后a[0],a[1]交替维护
int main()
{
	LL n,flag=0,row=3;//flag是标志为,用于交替执行递推三角函数的代码块,row是当前行
	LL t;//列
	LL index=1;//计数器,统计当前元素是杨辉三角中的第几个元素
	scanf("%lld",&n);
	if(n==1)//对n=1的情况进行特判,因为是从第三行开始递推的
	{
		printf("%d",index);
		system("pause");
		return 0;
	}
	a[0][1]=1;//每一行的第一个元素都是1
	a[1][1]=1;
	index+=2;
	while(true)
	{
		index++;
		if(flag==0){
		for(t=2;t<=row%2+row/2;t++){
			if(t!=row%2+row/2)
			{
				index++;
				a[1][t]=a[0][t]+a[0][t-1];
				//printf("%lld ",a[1][t]);
			}else{
				index++;
			    a[1][t]=2*a[0][t-1];
				//printf("%lld\n",a[1][t]);
			}
			if(a[1][t]==n)
			{printf("%lld",index);system("pause");return 0;}
			if(a[1][3]>n)//观察杨辉三角的第三列发现当第三列是数大于要找的n时,n就只能存在于第2列(按顺序递增的那一列)
			{
				LL sum=0;
				sum=(1+n)*(n/2)+(n%2)*(n/2+1);
				printf("%lld",sum+2);system("pause");return 0;
			}
		}
		index+=row-(row%2+row/2);
		flag=1;
		}
		else if(flag==1)
		{
			for(t=2;t<=row%2+row/2;t++){
				a[0][t]=a[1][t]+a[1][t-1];
				index++;
				//printf("%lld ",a[0][t]);
				if(a[0][t]==n)
			{printf("%lld",index);system("pause");return 0;}
				if(a[0][3]>n)
				{
					
				  LL sum=0;
				sum=(1+n)*(n/2)+(n%2)*(n/2+1);
					printf("%lld",sum+2);system("pause");return 0;
				}
		}
			index+=row-(row%2+row/2);
			//printf("\n");
			flag=0;
		}
		row++;
	}
	system("pause");
	return 0;
}

 在实际提交测试的过程中发现,对于这道题最容易出错的是对数据取值范围,对于数据比较大的情况,除了将用到的数据类型定义成longlong型,a数组的中每行的列数定义成多少关乎是否越界导致错误,预估数据规模是很重要的,根据上面的规律,当前行的第三列大于1000000000是在第44723行,该行保存的元素44723/2=22362个,故数组a定义的列数至少要大于22362,才不会溢出,一旦溢出,前功尽弃。细节!!

### 关于2021年蓝桥杯比赛中与杨辉三角相关的Java实现 尽管目前未找到针对2021年蓝桥杯具体比赛中的杨辉三角题目描述,但从以往的比赛经验和已知的解法来看,可以总结出几种通用的解决方案。以下是基于已有资料和常见方法整理的内容。 #### 方法一:二维数组存储杨辉三角 这种方法的核心在于使用二维数组来保存每一层的数据结构。对于任意位置 `(i, j)` 的数值计算公式如下: \[ \text{triangle}[i][j] = \text{triangle}[i-1][j-1] + \text{triangle}[i-1][j] \] 其中 `triangle[i][j]` 表示第 `i` 层第 `j` 列的值[^1]。初始条件为每行的第一个和最后一个元素均为 1。 ```java public class YangHuiTriangle { public static void main(String[] args) { int n = 10; // 输出前n行 int[][] triangle = new int[n][]; for (int i = 0; i < n; i++) { triangle[i] = new int[i + 1]; // 初始化当前行为长度(i+1) triangle[i][0] = triangle[i][i] = 1; // 首尾设为1 for (int j = 1; j < i; j++) { triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j]; } // 打印当前行 System.out.println(Arrays.toString(triangle[i])); } } } ``` 此代码片段展示了如何构建并打印杨辉三角的前若干行。 --- #### 方法二:双数组交替更新 另一种更节省空间的方法是仅保留两行数据——当前行 (`array1`) 和下一行 (`array2`)。这种方式特别适合内存受限的情况[^2]。 核心逻辑: - 当前行首尾固定为 1; - 下一行中间部分由上一行相邻两个数相加得出。 ```java import java.util.Arrays; public class YangHuiDoubleArray { public static void main(String[] args) { int n = 10; int[] array1 = {1}; // 上一行初始化 for (int i = 0; i < n; i++) { System.out.println(Arrays.toString(array1)); // 打印当前行 int[] array2 = new int[array1.length + 1]; // 新建下一行 array2[0] = array2[array2.length - 1] = 1; // 设置两端为首尾 for (int j = 1; j < array2.length - 1; j++) { array2[j] = array1[j - 1] + array1[j]; } array1 = array2; // 更新到下一行 } } } ``` 上述代码实现了通过双数组动态生成杨辉三角的功能。 --- #### 方法三:组合数学公式求解 根据组合数学理论,杨辉三角的每一个元素都可以表示成组合数 \( C(n, k) \),即从 `n` 中选取 `k` 个元素的方式数量。其定义为: \[ C(n, k) = \frac{n!}{k!(n-k)!} \] 因此可以直接利用该公式逐项计算而不必显式维护整个矩阵[^3]。 ```java public class CombinationMethod { private static long combination(int n, int k) { if (k > n || k < 0) return 0; long res = 1; for (int i = 1; i <= k; ++i) { res *= (n - i + 1); res /= i; } return res; } public static void main(String[] args) { int n = 10; StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { for (int j = 0; j <= i; j++) { sb.append(combination(i, j)).append(" "); } sb.append("\n"); } System.out.print(sb.toString()); } } ``` 这段程序采用组合函数直接输出指定层数的结果。 --- #### 总结 以上三种方式分别适用于不同场景下的需求分析和技术选型。如果追求直观性和可读性,则推荐 **方法一**;若关注性能优化尤其是减少额外开销时可以选择 **方法二** 或者完全依赖数学公式的 **方法三**。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值