蓝桥杯真题:杨辉三角形

具体分析可以看:

第十二届蓝桥杯B组省赛杨辉三角形题解_Nervous_46216553的博客-CSDN博客

输入输出样例

示例 1

输入

6

输出

13

评测用例规模与约定

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

运行限制

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

 第一眼的思路是打表算呢,打算从组合数一个一个算来着这样的时间复杂度挺高的。

第二种思路是找路径:用C(m,n)的组合数来看,当前位置比N大则向左走,小则向下走一次,向中间走一次:

#include<iostream>
using namespace std;
long long m;//行-1
long long n;//列-1
long long v;//C(m,n)的值
long long temp;//临时变量用来提速
void down(){//下一层
    v=v*(m+1)/(m-n+1);
    ++m;
}
void left(){//左一个
    v=v*n/(m-n+1);
    --n;
}
void center(){//尝试往中间靠
    temp=v*(m-n)/(n+1);//如果右边更大就向右,否则不用动
    if(v<temp){
        v=temp;
        ++n;
    }
}
int main(){
    long long N,ans=0;
    cin >> N;
    v=1;
    m=1;
    n=0;
    while(1){//查找
        if(v<N){
            down();
            center();
        }else if(v>N){
            left();
        }else{
            break;
        }
    }
    //检查位置正确性
    //cout << m+1 << " " << n+1 << endl;
    ans=m*(m+1)/2+n+1;//前面的等差数列求和然后加当前行
    if(ans==2)ans=1;
    cout << ans << endl;
    return 0;
}

第三种思路是二分:

利用列的递增性质我们进行二分:

#include<iostream>
using namespace std;
long long N;
long long P(long long m,long long n){//计算m行n列的值
    --m;//P(m,m)=C(m-1,n-1)
    --n;
    n=max(n,m-n);//C(m,n)==C(m,m-n)
    long long ans=1;
    for(long long i=1;i<=(m-n);++i){
        ans*=(n+i);
        ans/=i;//由上文定理一定能除尽
        if(ans>N){//及时退出防止溢出(只用大小关系,不需要准确值)
            return N+1;
        }
    }
    return ans;
}
long long find(long long n){//二分查找第n列
    long long gap=1<<30,m=n;//不存在m不小于n的位置!!!
    while(gap){
        if(P(m,n)<N){//小了往前跑
            m+=gap;
        }else if(P(m,n)>N){//大了间距缩小往前找
            gap/=2;//(8,4,2,1一定能凑出<=15的所有数(就是二进制表示吗))
            m-=gap;
        }else{
            return (m)*(m-1)/2+n;//前面的行等差数列求和然后加当前行
        }
    }
    return (N+1)*(N+1);//找不到就返会一个一定超了的位置,别的如果找到一定排的靠前。
}
int main(){
    long long ans=1;
    cin >> N;
    if(N!=1)//因为第一列全是1,值相等所以不可以用二分查找
        ans=find(2);
    for(int i=3;i<=100;++i){//第N行总和2^n(每行的总和都是上一行的两倍),最大值肯定>(2^n)/n,左侧100列以内绝对足够搜出10^9的数值(因为前文组合数计算优化过了,根本不怕溢出,查几列随便二分查找的速度很快很快)
        ans=min(ans,find(i));//找最小的
    }
    cout << ans << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值