《从入门到精通:蓝桥杯编程大赛知识点全攻略》(七)-买不到的数目、蚂蚁感冒、饮料换购

前言

买不到的数目、蚂蚁感冒、饮料换购是很经典的关于数学方法的一些题目,希望大家可以通过这篇博客积累一下做题的广度,并且学会用过暴力打表的方式来得到一些答案的规律。


买不到的数目

小明开了一家糖果店。

他别出心裁:把水果糖包成4颗一包和7颗一包的两种。

糖果不能拆包卖。

小朋友来买糖的时候,他就用这两种包装来组合。

当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。

你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。

本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。

输入格式
两个正整数 n,m,表示每种包装中糖的颗数。
输出格式
一个正整数,表示最大不能买到的糖数。
数据范围
2≤n,m≤1000,保证数据一定有解。
输入样例:

4 7

输出样例:

17

算法思路

裴蜀定理
裴蜀定理(或贝祖定理)得名于法国数学家艾蒂安·裴蜀,说明了对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性不定方程(称为裴蜀等式):若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。
那么我们可以通过暴力打标来找规律。
暴力思路如下:
通过深度优先搜索(DFS)来判断从一个初始值 ( m ) 是否可以通过减去给定的两个数 ( p ) 或 ( q ) 来达到 0。其核心思想是使用递归逐步减去 ( p ) 或 ( q ),并通过返回布尔值来判断是否可以成功减至 0。

public static boolean dfs(int m, int p, int q) {
    if (m == 0) return true;  // 如果 m == 0,表示已经可以通过减法得到 0,返回 true
    if (m >= p && dfs(m - p, p, q)) return true;  // 如果 m >= p,可以减去 p,递归判断减去 p 后是否能达到 0
    if (m >= q && dfs(m - q, p, q)) return true;  // 如果 m >= q,可以减去 q,递归判断减去 q 后是否能达到 0
    return false;  // 如果没有符合条件的情况,返回 false
}

给定a,b,若d=gcd(a,b) > 1,则一定不能凑出最大数。
结果如下:

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

得出规律
(p - 1) *(q - 1)- 1

代码如下

暴力打表代码:

package AcWingLanQiao;

import java.io.*;


public class 买不到的数目 {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);

    public static void main(String[] args) throws Exception {
        int p = nextInt();
        int q = nextInt();
        int res = 0;
        for(int i = 1;i <= 1000;i++){
            if(!dfs(i,p,q)){
                res = i;
            }
        }
        pw.println(res);
        pw.flush();
    }
    public static boolean dfs(int m,int p,int q){
        if(m == 0) return true;
        if(m >= p && dfs(m - p,p,q)) return true;
        if(m >= q && dfs(m - q,p,q)) return true;
        return false;
    }
    public static int nextInt()throws Exception{
        st.nextToken();
        return (int)st.nval;
    }
}

最终代码如下:

import java.io.*;

public class Main {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);

    public static void main(String[] args) throws Exception {
        int p = nextInt();
        int q = nextInt();
        pw.println((p - 1) * (q - 1) - 1);
        pw.flush();
    }
   
    public static int nextInt()throws Exception{
        st.nextToken();
        return (int)st.nval;
    }
}

蚂蚁感冒

长 100厘米的细长直杆子上有 n只蚂蚁。它们的头有的朝左,有的朝右。

每只蚂蚁都只能沿着杆子向前爬,速度是 1 厘米/秒。

当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有 1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。
输入格式
第一行输入一个整数 n, 表示蚂蚁的总数。
接着的一行是 n个用空格分开的整数 Xi, Xi的绝对值表示蚂蚁离开杆子左边端点的距离。
正值表示头朝右,负值表示头朝左,数据中不会出现 0 值,也不会出现两只蚂蚁占用同一位置。
其中,第一个数据代表的蚂蚁感冒了。
输出格式
输出1个整数,表示最后感冒蚂蚁的数目。
数据范围
1<n<50
0<|Xi|<100
输入样例1:

3
5 -2 8

输出样例1:

1

输入样例2:

5
-10 8 -20 12 25

输出样例2:

3

算法思路

在这里插入图片描述
重点:两个蚂蚁相撞,其实可以视为两个蚂蚁穿过,可以理解为两个蚂蚁互换身体。
具体分为两种情况:
(1)第一个蚂蚁向右走,那么它的右边向左走一定会被感染,右边向右走一定不会被感染,左边向左走不会被感染,左边向右走会被右边向左走的感染。
(2)第一个蚂蚁向左走,那么它的左边向右走一定会被感染,左边向左走不会被感染,右边向右走不会被感染,右边向左会被左边向右走的感染。
根据上述结论,我们其实只需要统计右边向左的蚂蚁和左边向右走的蚂蚁数目即可。
用一个数组arr来存储蚂蚁,n表示蚂蚁的个数。第一个蚂蚁表示生病的蚂蚁arr[0],用整型变量left表示左边向右走,整型变量right表示右边向左走;从第二个蚂蚁开始遍历,如果arr[i]的绝对值小于arr[0]说明在左边并且arr[i]大于0说明往右走,此时就是左边向右走故left++;如果arr[i]的绝对值大于arr[0]说明在右边并且arr[i]小于0说明往左走,此时就是右边向左走即right++。

		for(int i = 1;i <= n;i++){
            //左边向右走
            if(Math.abs(arr[i]) < Math.abs(arr[0]) && arr[i] > 0){
                left++;
            }
            //右边向左走
            else if(Math.abs(arr[i]) > Math.abs(arr[0]) && arr[i] < 0){
                right++;
            }
        }

当蚂蚁向右走时,当没有右边向左走的蚂蚁时,那么就只有生病的1只蚂蚁;当蚂蚁向左走的时候,没有左边向右走的蚂蚁时,就只有生病的一只蚂蚁。
排除上述的两种情况,最后生病的蚂蚁就是左边向右走的蚂蚁加上右边向左走的蚂蚁加上开始生病的蚂蚁。

		//第一个蚂蚁向右走,右边向左走为0
        //第一个蚂蚁向左走,左边向右走为0
        if(arr[0] > 0 &&  right == 0 || arr[0] < 0 && left == 0){
            pw.println(1);
        }else {
            pw.println(left + right + 1);
        }

代码如下

import java.io.*;

public class Main {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static int N = 55;
    static int[] arr = new int[N];
    static int n;
    public static void main(String[] args) throws Exception {
        n = nextInt();
        for(int i = 0;i <  n;i++){
            arr[i] = nextInt();
        }
        //左边向右走
        int left = 0;
        //右边向左走
        int right = 0;
        for(int i = 1;i <= n;i++){
            //左边向右走
            if(Math.abs(arr[i]) < Math.abs(arr[0]) && arr[i] > 0){
                left++;
            }
            //右边向左走
            else if(Math.abs(arr[i]) > Math.abs(arr[0]) && arr[i] < 0){
                right++;
            }
        }
        //第一个蚂蚁向右走,右边向左走为0
        //第一个蚂蚁向左走,左边向右走为0
        if(arr[0] > 0 &&  right == 0 || arr[0] < 0 && left == 0){
            pw.println(1);
        }else {
            pw.println(left + right + 1);
        }
        pw.flush();
    }
    public static int nextInt() throws Exception {
        st.nextToken();
        return (int) st.nval;
    }
}

饮料换购

乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料,凭3个瓶盖可以再换一瓶C型饮料,并且可以一直循环下去(但不允许暂借或赊账)。
请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入n 瓶饮料,最后他一共能喝到多少瓶饮料。
输入格式
输入一个整数 n,表示初始买入的饮料数量。
输出格式
输出一个整数,表示一共能够喝到的饮料数量。
数据范围
0<n<10000
输入样例:

100

输出样例:

149

算法思路

在这里插入图片描述
n表示输入的初始瓶数,同时也代表得到的初始瓶盖数res = n。当n大于等于3的时候进入循环,n / 3就是新的到的饮料数,接着更新瓶盖数即是n / 3 + n % 3,最后循环结束,得到的res就是最后喝的饮料数。

代码如下


import java.io.*;

public class Main {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);

    public static void main(String[] args) throws Exception{
        int n = nextInt();
        int res = n;
        //n表示瓶盖的数量
        while(n >= 3){
            //瓶的数量
            res += n / 3;
            //瓶盖的数量
            n = n / 3 + n % 3;
        }
        pw.println(res);
        pw.flush();
    }
    public static int nextInt() throws Exception {
        st.nextToken();
        return (int) st.nval;
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值