基础的杨辉三角
//输出十行杨辉三角
//声明二维数组
//给每个元素赋值
//每行的首尾元素赋值 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。
具体步骤如下:
-
首先确定二分的左右边界:
- 左边界
l设为2 * k,因为在斜行k中,从左到右看,第一个数的位置是2 * k; - 右边界
r设为max(l, n),确保右边界不小于左边界,同时考虑到可能存在特例,当n = 1时,右边界不能小于左边界。
- 左边界
-
进行二分查找:
- 在区间
[l, r)中查找一个数,使得C(mid, k) >= n成立,即找到大于等于n的第一个数的位置; - 如果找到符合条件的位置
mid,则将右边界r更新为mid,继续向左收缩搜索范围; - 如果找不到符合条件的位置,则将左边界
l更新为mid + 1,继续向右搜索。
- 在区间
-
判断是否找到满足条件的数:
- 如果最终找到了满足条件的位置
r,则判断C(r, k)是否等于n,如果等于,则输出该数的位置,并返回true; - 如果
C(r, k)不等于n,则说明斜行k中不存在值为n的数,返回false。
- 如果最终找到了满足条件的位置
-
输出结果:
- 输出满足条件的数的位置,位置计算公式为
(r + 1) * r / 2 + k + 1,其中(r + 1) * r / 2表示斜行r的首项到第r项的和,加上k + 1表示斜行内的偏移。
- 输出满足条件的数的位置,位置计算公式为
这样,函数 bool check(int k) 就能够完成对斜行 k 中是否存在值为 n 的数的检查,并输出其位置。
5005

被折叠的 条评论
为什么被折叠?



