算法笔记_109:第四届蓝桥杯软件类省赛真题(JAVA软件开发本科B组部分习题)试题解答

 目录

1 马虎的算式

2 黄金连分数

3 有理数类

4 幸运数

5 连号区间数

 

前言:以下试题解答代码部分仅供参考,若有不当之处,还请路过的同学提醒一下~


1 马虎的算式

标题: 马虎的算式


    小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。

    有一次,老师出的题目是:36 x 495 = ?

    他却给抄成了:396 x 45 = ?

    但结果却很戏剧性,他的答案竟然是对的!!

    因为 36 * 495 = 396 * 45 = 17820

    类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54

    假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)

    能满足形如: ab * cde = adb * ce 这样的算式一共有多少种呢?


请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。

满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。


答案直接通过浏览器提交。
注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。

142

 

 

方法1(暴力枚举):
import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static int count = 0;
    //判断A和B是否是否1~9中不同的5个数字组成
    public boolean judge(int A, int B) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        while(A > 0) {
            list.add(A % 10);
            A = A / 10;
        }
        while(B > 0) {
            list.add(B % 10);
            B = B / 10;
        }
        Collections.sort(list);
        for(int i = 1;i < list.size();i++) {
            if(list.get(i - 1) == 0 || list.get(i - 1) == list.get(i))
                return false;
        }
        return true;
    }
    
    public void printResult() {
        for(int a = 10;a < 100;a++) {
            for(int b = 100;b < 1000;b++) {
                if(judge(a, b)) {
                    int a1 = a % 10;
                    int a2 = a / 10;
                    int b1 = b % 10;
                    int b2 = b / 10 % 10;
                    int b3 = b / 100;
                    if(a * b == (a2*100 + b2*10 + a1) * (b3*10 + b1)) {
                        count++;
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        test.printResult();
        System.out.println(count);
    }
}

 

 

方法2(dfs全排列枚举):
import java.util.ArrayList;

public class Main1 {
    public static int count = 0;
    
    public void check(ArrayList<Integer> list) {
        int a = list.get(0);
        int b = list.get(1);
        int c = list.get(2);
        int d = list.get(3);
        int e = list.get(4);
        int num1 = (a*10 + b) * (c*100 + d*10 + e);
        int num2 = (a*100 + d*10 + b) * (c*10 + e);
        if(num1 == num2)
            count++;
        return;
    }
    //dfs求取1~9中随机五个不同数字的全排列
    public void dfs(ArrayList<Integer> list, int step) {
        if(step == 5) {
            check(list);
            return;
        } else {
            for(int i = 1;i < 10;i++) {
                if(list.contains(i))
                    continue;
                list.add(i);
                dfs(list, step + 1);
                list.remove(list.size() - 1);
            }
        }
        return;
    }
    
    public static void main(String[] args) {
        Main1 test = new Main1();
        ArrayList<Integer> list = new ArrayList<Integer>();
        test.dfs(list, 0);
        System.out.println(count);
    }
}

 

 

2 黄金连分数

标题: 黄金连分数


    黄金分割数0.61803... 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。

    对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!!


    言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。

    比较简单的一种是用连分数:

                  1
    黄金数 = ---------------------
                        1
             1 + -----------------
                          1
                 1 + -------------
                            1
                     1 + ---------
                          1 + ...

                           

    这个连分数计算的“层数”越多,它的值越接近黄金分割数。

    请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。

    小数点后3位的值为:0.618
    小数点后4位的值为:0.6180
    小数点后5位的值为:0.61803
    小数点后7位的值为:0.6180340
   (注意尾部的0,不能忽略)


你的任务是:写出精确到小数点后100位精度的黄金分割值。

注意:尾数的四舍五入! 尾数是0也要保留!

显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。

0.6180339887498948481971959525508621220510663574518538453723187601229582821971784348083863296133320592(求取fib(48) / fib(49)的精度)

0.6180339887498948482045868343656381177203091798057628621354486227052604628189024496923340122463725714(求取fib(198)/fib(199)的精度)

0.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375(求取fib(49998)/fib(49999)的精度)
此处,个人建议求取精度的问题,尽量做到当前自己能够使用计算机算的最大精度,所以最好选择fib(49998)/fib(49999)的结果。

轻易可知fib(100)以上的数基本上已经超过long型整数的范围了,此处可以借用大整数类型完成更加精确的计算。

 

 

import java.math.BigDecimal;
import java.math.BigInteger;

public class Main {
    
    public static void main(String[] args) {
        BigInteger[] A = new BigInteger[50000];
        A[0] = new BigInteger("0");
        A[1] = new BigInteger("1");
        for(int i = 2;i < 50000;i++) {
            A[i] = A[i - 1].add(A[i - 2]);//迭代求取斐波那契数(i值越大,求取结果精度越大)
        }
        BigDecimal a = new BigDecimal(A[49998]);
        BigDecimal b = new BigDecimal(A[49999]);
        System.out.println(a.divide(b, 100, BigDecimal.ROUND_HALF_UP));
    }
}

 

 

3 有理数类

标题:有理数类

    有理数就是可以表示为两个整数的比值的数字。一般情况下,我们用近似的小数表示。但有些时候,不允许出现误差,必须用两个整数来表示一个有理数。

    这时,我们可以建立一个“有理数类”,下面的代码初步实现了这个目标。为了简明,它只提供了加法和乘法运算。

class Rational
{
    private long ra;
    private long rb;
    
    private long gcd(long a, long b){
        if(b==0) return a;
        return gcd(b,a%b);
    }
    public Rational(long a, long b){
        ra = a;
        rb = b;    
        long k = gcd(ra,rb);
        if(k>1){ //需要约分
            ra /= k;  
            rb /= k;
        }
    }
    // 加法
    public Rational add(Rational x){
        return ________________________________________;  //填空位置
    }
    // 乘法
    public Rational mul(Rational x){
        return new Rational(ra*x.ra, rb*x.rb);
    }
    public String toString(){
        if(rb==1) return "" + ra;
        return ra + "/" + rb;
    }
}

使用该类的示例:
    Rational a = new Rational(1,3);
    Rational b = new Rational(1,6);
    Rational c = a.add(b);
    System.out.println(a + "+" + b + "=" + c);


请分析代码逻辑,并推测划线处的代码,通过网页提交
注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!


new Rational(x.ra*rb + ra*x.rb, x.rb*rb)

 

 

4 幸运数

标题:幸运数

    幸运数是波兰数学家乌拉姆命名的。它采用与生成素数类似的“筛法”生成。

    首先从1开始写出自然数1,2,3,4,5,6,....
    
    1 就是第一个幸运数。
    我们从2这个数开始。把所有序号能被2整除的项删除,变为:

    1 _ 3 _ 5 _ 7 _ 9 ....

    把它们缩紧,重新记序,为:
    
    1 3 5 7 9 .... 。这时,3为第2个幸运数,然后把所有能被3整除的序号位置的数删去。注意,是序号位置,不是那个数本身能否被3整除!! 删除的应该是5,11, 17, ...

    此时7为第3个幸运数,然后再删去序号位置能被7整除的(19,39,...) 

    最后剩下的序列类似:

    1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, ...

本题要求:

输入两个正整数m n, 用空格分开 (m < n < 1000*1000)
程序输出 位于m和n之间的幸运数的个数(不包含m和n)。

例如:
用户输入:
1 20
程序输出:
5

例如:
用户输入:
30 69
程序输出:
8



资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗  < 2000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

 

 

import java.util.ArrayList;
import java.util.Scanner;

public class Main {
    
    public void printResult(int m, int n) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(-1);   //第0位存-1,后面才开始存幸运数,方便后面删除操作
        for(int i = 1;i < n;i++) {
            if(i % 2 == 0) 
                continue;
            list.add(i);
        }
        int count = 2;  //开始遍历的幸运数位置
        while(true) {
            int start = list.get(count);
            ArrayList<Integer> list1 = new ArrayList<Integer>();
            list1.add(-1);
            for(int i = 1;i < list.size();i++) {
                if(i % start == 0)
                    continue;
                else {
                    list1.add(list.get(i));
                }
            }
            list = list1;
            count++;
            if(count > list.size() - 1)
                break;
        }
        list.remove(0);    //删除第0位-1情况
        int i = 0;
        for(;i < list.size();i++) {
            if(list.get(i) > m)
                break;
        }
        int result = list.size() - i;
        System.out.println(result);
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        int m = in.nextInt();
        int n = in.nextInt();
        test.printResult(m, n);
    }
}

 

 

 

5 连号区间数

标题:连号区间数

    小明这些天一直在思考这样一个奇怪而有趣的问题:

    在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是:

    如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。

    当N很小的时候,小明可以很快地算出答案,但是当N变大的时候,问题就不是那么简单了,现在小明需要你的帮助。

输入格式:
第一行是一个正整数N (1 <= N <= 50000), 表示全排列的规模。
第二行是N个不同的数字Pi(1 <= Pi <= N), 表示这N个数字的某一全排列。

输出格式:
输出一个整数,表示不同连号区间的数目。

示例:
用户输入:
4
3 2 4 1

程序应输出:
7

用户输入:
5
3 4 2 5 1

程序应输出:
9

解释:
第一个用例中,有7个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [2,2], [3,3], [4,4]
第二个用例中,有9个连号区间分别是:[1,1], [1,2], [1,3], [1,4], [1,5], [2,2], [3,3], [4,4], [5,5]


资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗  < 5000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

 

 

import java.util.Scanner;

public class Main {
    
    /*
     * 这题的重点在于题意的理解,题意理解了,解答就会很简单
     *核心: 如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)
     * 递增排序后能得到一个长度为R-L+1的“连续”数列,则称这个区间连号区间。
     * 那么就是说,在给定的一个全排列中A[L]~A[R]之间未排序,如果经过递增排序处理后
     * 发现,A[L]~A[R]之间的元素每次自增1,即为一个连号区间。
     */
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();  //输入全排列的规模
        int[] data = new int[n];
        for(int i = 0;i < n;i++)  //输入该全排列具体元素,易知每一个元素均布相等
            data[i] = in.nextInt();
        int count = 0;
        for(int i = 0;i < n;i++) {
            int max = data[i];  //从i开始到后续i~n之间每一个区间的初始最大值
            int min = data[i];  //从i开始到后续i~n之间每一个区间的初始最小值
            for(int j = i;j < n;j++) {
                if(max < data[j])
                    max = data[j];
                if(min > data[j])
                    min = data[j];
                if(max - min == j - i) {  //此处判断就是相当于经过递增排序处理后,符合连号区间顶点
                    count++;
                }
            }
        }
        System.out.println(count);
    }
}

 

posted @ 2017-03-24 15:54  舞动的心  阅读(1279)  评论(0编辑  收藏  举报