【蓝桥杯算法笔记】数学

E.数学

基础知识

蓝桥杯中的数学问题更多的像脑袋急转弯

例题

一、买不到的数目

1.题意

因为大于x的任何整数都可以用p和q组合凑出来,现在已知p和q,求最大不能组合出的数字x

2.思路(考试技巧)

(1)尽力分析

d=gcd(p,q)>1,无解 // 只要p和q的最大公约数>1,就会无解,但此题保证是有解的

只要p和q互质,那么一定有解,但这个上限也不好判断,可以暴力用一个很大的数来表示上界,枚举到一个很大的数的时候就有解了。

为什么p和q互质,就有解呢?

根据裴蜀定理:如果p,q的最大公约数是d的话gcd(p,q)=d,那么一定存在两个整数(不一定是正整数)a、b,使得ap+bq=d。因此如果p,q是互质的话,那么他们的最大公约数为1,gcd(p,q)=1,则ap+bq=1,当我们要凑的数m足够大时,apm+bqm=m,恒等变形a(m-q)p+b(m+p)q=m,只要m足够大,那么每次让较大的数变小一点,让较小的数变大一点,总会让结果为正,所以只要p和q是互质的就一定有解。那怎么估计这个上界m呢?暴力打表。

(2)打表找规律

数学问题分析不出来(不是数论问题),可以打表找规律,打表考验写暴搜的能力。

根据上面的分析,写出打表代码

这个dfs就俩分支:枚举选p和选q,然后递归。

m表示当前要凑的总数量,如果m == 0,就表示m已经被凑出来了,否则枚举当前选哪种糖,如果选p并且m - p可以被凑出来,那就是说明m可以被凑出;同理如果选q并且m - q可以被凑出来,那就说明m可以被凑出。

#include <iostream>

using namespace std;
//这个dfs就俩分支:枚举选p和选q,然后递归。
//给定一个m,是否能用p和q凑出来(暴搜一下所有方案,看能否凑出来 ),m表示当前要凑的总数量
bool dfs(int m,int p,int q)
{
    if(m == 0) return true;  //如果m == 0,就表示m已经被凑出来了,否则枚举当前选哪种糖

    if(m >= p && dfs(m - p,p,q)) return true;//如果选p并且m - p可以被凑出来,那就是说明m可以被凑出
    if(m >= q && dfs(m - q,p,q)) return true;//同理如果选q并且m - q可以被凑出来,那就说明m可以被凑出。

    return false;//如果都不行的话,返回false 
}

int main()
{
    int p,q;
    cin >> p >> q;
    int res = 0;
    for(int i = 1; i <= 1000;i ++) //假设就看1000以内的数 ,超过1000就认为一定可以凑出来 
    {
        if(!dfs(i,p,q)) res = i;  //找到最大的一个不能被凑出来的数 ,记录 
    }

    cout << res << endl;

    return 0;
}

/*
3 2 1
3 4 5
3 5 7
3 7 11
3 8 13

*/ 

可以找出规律,得到结论:

如果 a,b均是正整数且互质,那么由 ax+by,x≥0,y≥0不能凑出的最大数是 (a−1)(b−1)−1。

3.证明

AcWing 525. 小凯的疑惑 - AcWing

4.代码
#include <iostream>

using namespace std;

int main()
{
    int p, q;
    cin >> p >> q;
    cout << (p - 1) * (q - 1) - 1 << endl;

    return 0;
}
//方法二:最小公倍数减去这两个数

#include <iostream>
using namespace std;
//求最大公约数:欧几里得算法(辗转相除法)
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}


int main()
{
    int p,q;
    cin>>p>>q;
    
    //求最小公倍数:两数之积除以最大公约数,忘记时可以举例回忆:4 和 2 gcd为2,t为4
    int t=p*q/gcd(p,q);
    
    //结果为:最小公倍数减去这两个数
    int res=t-p-q;
    cout<<res;
    
    return 0;
}

二、蚂蚁感冒

1.思路

(1)性质:因为蚂蚁的速度大小相同,所以调头等价于穿过。

(2)分析:

①无论第一只蚂蚁的方向如何,第一只蚂蚁左边方向向左的蚂蚁和第一只蚂蚁右边方向向右的蚂蚁永远不会感冒。用ltr表示左边向右走的蚂蚁数量,rtf表示右边向左走的蚂蚁数量
②当第一只蚂蚁方向向右时,第一只蚂蚁右边如果存在方向向左的蚂蚁时,则第一只蚂蚁左边方向向右的蚂蚁都会感冒,此时sum=ltr+rtl+1,否则sum=1
③当第一只蚂蚁方向向左时,第一只蚂蚁左边如果存在方向向右的蚂蚁时,则第一只蚂蚁右边方向向左的蚂蚁都会感冒,此时sum=ltr+rtl+1,否则sum=1

2.代码
#include <iostream>
using namespace std;
const int N=55;
int n;
int x[N];

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>x[i];

    //ltr表示左边向右走的蚂蚁数量,rtf表示右边向左走的蚂蚁数量
    int ltr=0,rtf=0;
    for(int i=1;i<n;i++)
    {
        if((abs(x[i])<abs(x[0]))&&x[i]>0) ltr++;//找到感冒蚂蚁左边边且向右走的
        else if((abs(x[i])>abs(x[0]))&&x[i]<0) rtf++;//找到感冒蚂蚁右边且向左走的
    }
    //特殊情况
    if((x[0]>0&&rtf==0)||(x[0]<0&&ltr==0)) cout<<1<<endl;
    else cout<<ltr+rtf+1<<endl;

    return 0;
}

三、饮料换购

1.题意

3个瓶盖可以换一瓶饮料,现在买了n瓶饮料,最终能喝到多少瓶饮料呢?

2.解法一:模拟1 Olog3(N)

思路:

在这里插入图片描述

代码:

#include <iostream>

using namespace std;

int main()
{
    int n;
    cin >> n;
    int res = n;//假设一开始把n瓶都喝完,n一直是瓶盖的个数
    while (n >= 3)
    {
        res += n / 3; // res += [当前可兑换的瓶数]
        n = n / 3 + n % 3; //n = [当前可兑换的瓶数] + [兑换后剩余的瓶数]
    }
    cout << res << endl;
    return 0;
}
3.解法二:模拟2

思路:

模拟出换了多少瓶,最终结果为换的瓶数+原来的瓶数-1就是喝了多少瓶

代码:

#include <iostream>
using namespace std;
int main()
{
    
    int n;
    cin>>n;
    int t=n; //t表示一开始原来有多少瓶
    int cnt=0; //cnt表示换的瓶数
    while(n>0) //当等于0时,表示原来的换的都喝完了,结束
    {
        n=n-3;  //每喝三瓶能换一瓶 -3
        cnt++;  //统计换的次数
        n=n+1;  //喝完三瓶换了一瓶 +1
    }
    
    cout<<cnt+t-1; //一开始有多少瓶+换的瓶数-1
     
    return 0;
}
4.解法三:数学公式 O(1)

思路:

当有3个空瓶时,可以兑换1瓶饮料,这时已经喝的饮料+1, 剩余空瓶为1
当有4个空瓶时,可以兑换1瓶饮料,这时已经喝的饮料+1, 剩余空瓶为2
当有5个空瓶时,可以兑换2瓶饮料,这是已经喝的饮料+2, 剩余空瓶为1

总空瓶里面,用1个瓶子来装兑换后的酒,每次兑换消耗2个空瓶。

每次兑换消耗3个空瓶,兑换1瓶饮料,又补充了1个空瓶,所以理解为每次兑换消耗2个空瓶、
(这个前提是空瓶的剩余数量 >= 3,也就是说如果空瓶数量为2时,不满足兑换条件。)
=> 总共n个空瓶, 拿出1个空瓶, 剩下的 n-1 个空瓶用来兑换, 每次兑换消耗2个空瓶。

代码:

#include <iostream>

using namespace std;

int main()
{
    int n;
    cin >> n;
    cout << n + (n - 1) / 2 << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值