杨辉三角从基础到竞赛

基础的杨辉三角

​
//输出十行杨辉三角 
//声明二维数组
//给每个元素赋值
//每行的首尾元素赋值 1
//非首尾元素 yanghui[i][j] = yanghui[i-1][j-1]+yanghui[i-1][j]
//遍历数组
//1
//1 1
//1 2 1
//1 3 3 1
//1 4 6 4 1
//1 5 10 10 5 1
//1 6 15 20 15 6 1
//......
#include <stdio.h>

int main()
{

    long long yanghui[100][100];
    int n;
    printf ("请输入行数:\n");
    scanf("%d",&n);
    
    for (int i=0;i<n;i++)
    {
        yanghui[i][0]=1;
        yanghui[i][i]=1;
        
        for (int j=1;j<i;j++)
        {
        yanghui[i][j] = yanghui[i-1][j-1]+yanghui[i-1][j];
         } 
    }
    
    for (int i=0;i<n;i++)
    {
        for (int j=0;j<=i;j++)
        {
            printf("%ld ",yanghui[i][j]);
        
        }
        printf ("\n");
    }
    return 0;
}

​

第十二届蓝桥杯省赛第一场C++ B组/C组中的杨辉三角

取自AcWing大神(非商业转载)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

int n;
/*
    组合数和杨辉三角:第i行第j列的数都是组合数C(i, j) (i,j从0开始)
        C(n, 1) = n --> 对应从左向右看斜着的第二列! ---> 一定有解
    由于杨辉三角左右对称(C(a, b) == C(a, a-b)),又由于找第一次出现,因此一定在左边,右边可以直接删掉!

            1  ---> C(0, 0)
          1 
        1   2  ---> C(2, 1)
      1   3                             ---> C(2n, n)
    1   4   6  ---> C(4, 2)
  1   5   10
1   6   15  20 ---> C(6, 3)

    n最大1e9,C(34, 17) > 1e9, C(32, 16) < 1e9,因此只要枚举前16个斜行即可!


    性质:
        1. 每一斜行从上到下递增
        2. 每一横行从中间到两边依次递减

    因此我们直接从中间对称轴倒序二分找起即可!

        C(r, k)对应的顺序值为:(r + 1) * r / 2 + k + 1

        二分的左右端点:l:2k,r:max(n, l)
            右端点一定不能比左端点小!
            特例:否则当n=1时,会出问题!

*/
// C(a, b) = a!/b!(a-b)! = a * (a-1) .. b个 / b!
LL C(int a, int b){
    LL res = 1;
    for(int i = a, j = 1; j <= b; i --, j ++){
        res = res * i / j;
        // 大于n已无意义,且防止爆LL
        if(res > n) return res;
    }
    return res;
}

bool check(int k){
    // 二分该斜行,找到大于等于该值的第一个数
    // 左边界2k,右边界为max(l, n)取二者最大即可!
    int l = 2 * k, r = max(n, l);
    while(l < r){
        int mid = l + r >> 1;
        if(C(mid, k) >= n) r = mid;
        else l = mid + 1;
    }
    if(C(r, k) != n) return false;
    // C(r, k)的从0开始的顺序!
    cout << 1ll * (r + 1) * r / 2 + k + 1 << endl;
    return true;
}

int main(){
    cin >> n;
    // 从第16斜行枚举即可!
    for(int k = 16; ; k --)
        if(check(k)) break;
    return 0;
}

将左半部分(左半部分的编号肯定比右半部分小,不考虑右半部分)一个斜行,一个斜行地去看,会发现同一斜行中,越往左下,数越大,同一行中,越往右数越大。所以从最里面的斜行往外找,比如要找20(第一次出现在第3斜行的开头,位置在第6行(都从第0算起)),则其他斜行中要出现20,那编号一定比第3斜行的编号大。

而对称轴,即每个斜行开头(右上角)的数字的规律是:比如第K斜行,则为。

而每个斜行末尾的数字规律是:比如第k斜行,则为,为什么会有出现n呢?因为第n行的第1个(从第0开始算)数字必定是n,且该数在第n行为最后一次出现。并且第k斜行中,每个数都是所在行的第k个位置。

求组合数问题以及二分(杨辉三角问题基础)

#include <iostream>

using namespace std;

long long C(int a, int b){
    long long res = 1;
    for(int i = a, j = 1; j <= b; i --, j ++){
        res = res * i / j;
    }
    return res;
}

//对于边界情况都处理 
//long long C(int a, int b){
//    // 处理边界情况
//    if (b == 0 || a == b) return 1;
//    
//    long long res = 1;
//    for(int i = a, j = 1; j <= b; i --, j ++){
//        // 在每次乘法之前检查是否会产生大于long long类型的溢出
//        if (res > LLONG_MAX / i) {
//            // 如果会溢出,则返回一个特殊值表示溢出
//            return LLONG_MAX;
//        }
//        res = res * i / j;
//    }
//    return res;
//}

int main()
{
    int x,y;
    cin >> x >> y;
    long long res = C(x,y);
    
    cout << res;
    
    return 0; 
}

排列数:

以下写法取自他人:

#include <iostream>
#include <vector>
using namespace std;
long long n;
long long CC(int a, int b)
{			//组合数公式 a为C的下标,b为C的上标:C=a!/((a-b)!*b!)
	long long  result = 1;
	for (int i = a, j = 1; j <= b; i--, j++)
	{
		result = result * i / j;
		if (result > n)	return result;	//当该组合数还没有算完就发现已经大于n,则在往下算已经没有意义。直接跳出
	}
	return result;
}
bool check(int k)
{
	//利用二分查找,去找等于n的数
	long long L = k * 2;		//每个斜行的下届(开头)是2k,
	long long R = n;			//每个斜行的上界(末尾)是n,也就是第n行,因为第n行的第2个数必定会出现n
	if (L > R)	return false;	//因为n肯定会在第n行出现且最后一次出现,而第k斜行的开头数所在的行数为2k,
							//如果斜行开头数所在行数比第n行还大,则肯定无法在该第2k行找到等于n的数
	while (L < R)      //这里的条件得是<,等循环结束时,R=L,在后续的if语句判断相不相等
	{
		long long mid = L + R >> 1;		//将L+R的数右移1位,相当于(L+R)/2;
		if (CC(mid, k) >= n)	R = mid;   //如果在mid中点找到,则R会一直为该次的mid位置,只剩下L在移动
		else L = mid + 1;
	}
	if (CC(R, k) != n)	return false;		//等二分结束发现不等于,则返回0
 
	cout << R * (R + 1) / 2 + k + 1 << endl;	//第R行前面有R行,数字之和为等差数列(1+2+3+...),在第R行中,第k位置数字是改行从0开始算,所以k+1
	return true;
 
}
int main()
{
 
	cin >> n;
	for (int k = 16; ; k--)
	{
		if (check(k))			//如果找到后则跳出for循环
			break;
	}
	return 0;
}

二分详解:

函数 bool check(int k) 的作用是检查给定的斜行 k 中是否存在一个数等于 n,如果存在,则输出该数的位置,并返回 true;如果不存在,则返回 false

具体步骤如下:

  1. 首先确定二分的左右边界:

    • 左边界 l 设为 2 * k,因为在斜行 k 中,从左到右看,第一个数的位置是 2 * k
    • 右边界 r 设为 max(l, n),确保右边界不小于左边界,同时考虑到可能存在特例,当 n = 1 时,右边界不能小于左边界。
  2. 进行二分查找:

    • 在区间 [l, r) 中查找一个数,使得 C(mid, k) >= n 成立,即找到大于等于 n 的第一个数的位置;
    • 如果找到符合条件的位置 mid,则将右边界 r 更新为 mid,继续向左收缩搜索范围;
    • 如果找不到符合条件的位置,则将左边界 l 更新为 mid + 1,继续向右搜索。
  3. 判断是否找到满足条件的数:

    • 如果最终找到了满足条件的位置 r,则判断 C(r, k) 是否等于 n,如果等于,则输出该数的位置,并返回 true
    • 如果 C(r, k) 不等于 n,则说明斜行 k 中不存在值为 n 的数,返回 false
  4. 输出结果:

    • 输出满足条件的数的位置,位置计算公式为 (r + 1) * r / 2 + k + 1,其中 (r + 1) * r / 2 表示斜行 r 的首项到第 r 项的和,加上 k + 1 表示斜行内的偏移。

这样,函数 bool check(int k) 就能够完成对斜行 k 中是否存在值为 n 的数的检查,并输出其位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值