算法筆記_111:第五屆藍橋杯軟件類省賽真題(Java本科A組)試題解答


 目錄

1 猜年齡

2 李白打酒

3 神奇算式

4 寫日志

5 錦標賽

6 六角填數

7 繩圈

8 蘭頓螞蟻

9 斐波那契

10 波動數列

 

前言:以下試題解答代碼部分僅供參考,若有不當之處,還請路過的同學提醒一下~


1 猜年齡

標題:猜年齡

    小明帶兩個妹妹參加元宵燈會。別人問她們多大了,她們調皮地說:“我們倆的年齡之積是年齡之和的6倍”。小明又補充說:“她們可不是雙胞胎,年齡差肯定也不超過8歲啊。”

    請你寫出:小明的較小的妹妹的年齡。


注意: 只寫一個人的年齡數字,請通過瀏覽器提交答案。不要書寫任何多余的內容。

10

 

public class Main {
    
    public void printResult() {
        for(int x = 1;x < 100;x++) {
            for(int y = 1;y < 100;y++) {
                int a = x * y;
                int b = 6 * (x + y);
                if(a == b && Math.abs(x - y) < 8 && x - y != 0) {
                    System.out.println("x = "+x+", y = "+y);
                }
            }
        }
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        test.printResult();
    }
}

 

 

2 李白打酒

標題:李白打酒

    話說大詩人李白,一生好飲。幸好他從不開車。

    一天,他提着酒壺,從家里出來,酒壺中有酒2斗。他邊走邊唱:

    無事街上走,提壺去打酒。
    逢店加一倍,遇花喝一斗。

    這一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。 

    請你計算李白遇到店和花的次序,可以把遇店記為a,遇花記為b。則:babaabbabbabbbb 就是合理的次序。像這樣的答案一共有多少呢?請你計算出所有可能方案的個數(包含題目給出的)。

    注意:通過瀏覽器提交答案。答案是個整數。不要書寫任何多余的內容。


14

 

public class Main {
    public static int count = 0;
    
    public void dfs(int sum, int step, int step1, int step2) {
        if(step > 15 || (step < 15 && sum == 0))//題意規定,共走15次,最后一次還剩1斗
            return;
        if(step == 15) {
            if(sum == 0 && step1 == 5 && step2 == 10)
                count++;
            return;
        }
        dfs(sum * 2, step + 1, step1 + 1, step2);
        dfs(sum - 1, step + 1, step1, step2 + 1);
        return;
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        test.dfs(2, 0, 0, 0);
        System.out.println(count);
    }
}

 

 

3 神奇算式

標題:神奇算式

    由4個不同的數字,組成的一個乘法算式,它們的乘積仍然由這4個數字組成。

    比如: 

210 x 6 = 1260 
8 x 473 = 3784
27 x 81 = 2187 

    都符合要求。

    如果滿足乘法交換律的算式算作同一種情況,那么,包含上邊已列出的3種情況,一共有多少種滿足要求的算式。

    請填寫該數字,通過瀏覽器提交答案,不要填寫多余內容(例如:列出所有算式)。

12

 

import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static int count1 = 0;
    public static int count2 = 0;
    
    public boolean judge(int a, int b) {
        int num = a * b;
        if(num < 1000)
            return false;
        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);     //對list中元素進行從小到大排序
        for(int i = 1;i < list.size();i++) {
            if(list.get(i - 1) == list.get(i))
                return false;
        }
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        while(num > 0) {
            list1.add(num % 10);
            num = num / 10;
        }
        Collections.sort(list1);
        if(list.size() == list1.size()) {
            int i = 0;
            for(;i < list.size();i++) {
                if(list.get(i) == list1.get(i))
                    continue;
                else
                    return false;
            }
            if(i == list.size())
                return true;
        }
        return false;
    }
    
    public void printResult() {
        for(int i = 1;i < 10;i++) {
            for(int j = 100;j < 1000;j++) {
                if(judge(i, j))
                    count1++;
            }
        }
        
        for(int i = 10;i < 100;i++) {
            for(int j = 10;j < 100;j++) {
                if(judge(i, j))
                    count2++;
            }
        }
        int result = count1 + count2 / 2;
        System.out.println("result = "+result);
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        test.printResult();
    }
}

 

 

 

4 寫日志

標題:寫日志

    寫日志是程序的常見任務。現在要求在 t1.log, t2.log, t3.log 三個文件間輪流寫入日志。也就是說第一次寫入t1.log,第二次寫入t2.log,... 第四次仍然寫入t1.log,如此反復。

    下面的代碼模擬了這種輪流寫入不同日志文件的邏輯。

public class A
{
    private static int n = 1;
    
    public static void write(String msg)
    {
        String filename = "t" + n + ".log";
        n = ____________;
        System.out.println("write to file: " + filename + " " + msg);
    }
}

    請填寫划線部分缺失的代碼。通過瀏覽器提交答案。

注意:不要填寫題面已有的內容,也不要填寫任何說明、解釋文字。

(n + 1) % 3 == 0 ? 3 : (n + 1) % 3

 

 

 

 

5 錦標賽

標題:錦標賽

   如果要在n個數據中挑選出第一大和第二大的數據(要求輸出數據所在位置和值),使用什么方法比較的次數最少?我們可以從體育錦標賽中受到啟發。

   如圖【1.png】所示,8個選手的錦標賽,先兩兩捉對比拼,淘汰一半。優勝者再兩兩比拼...直到決出第一名。

   第一名輸出后,只要對黃色標示的位置重新比賽即可。

   下面的代碼實現了這個算法(假設數據中沒有相同值)。

   代碼中需要用一個數組來表示圖中的樹(注意,這是個滿二叉樹, 不足需要補齊)。它不是存儲數據本身,而是存儲了數據的下標。   
   
   第一個數據輸出后,它所在的位置被標識為-1

class A{
       //a 表示待處理的數據,長度如果不是2的次冪,則不足位置補為-1
    static void pick(int[] a)
    {
        int n = 1;
        while(n<a.length) n *= 2;
        
        
        int[] b = new int[2*n-1];
        for(int i=0; i<n; i++){ 
            if(i<a.length) 
                b[n-1+i] = i;
            else
                b[n-1+i] = -1;
        }
        
        //從最后一個向前處理
        for(int i=b.length-1; i>0; i-=2){
            if(b[i]<0){
                if(b[i-1]>=0)
                    b[(i-1)/2] = b[i-1]; 
                else
                    b[(i-1)/2] = -1;
            }
            else{
                if(a[b[i]]>a[b[i-1]])
                    b[(i-1)/2] = b[i];
                else
                    b[(i-1)/2] = b[i-1];
            }
        }
        
        //輸出樹根
        System.out.println(b[0] + ": " + a[b[0]]);
        
        //值等於根元素的位置需要重新pk
        pk(a,b,0,b[0]);
        
        //再次輸出樹根
        System.out.println(b[0] + ": " + a[b[0]]);
    }

    // a 表示待處理數據,b 二叉樹,k 當前要重新比拼的位置,v 已經決勝出的值    
       static void pk(int[] a, int[] b, int k, int v)
    {
        
        int k1 = k*2+1;
        int k2 = k1 + 1;
        
        if(k1>=b.length || k2>=b.length){
            b[k] = -1;
            return;
        }
        
        if(b[k1]==v) 
            pk(a,b,k1,v);
        else
            pk(a,b,k2,v);
        
        
        //重新比較
        if(b[k1]<0){
            if(b[k2]>=0)
                b[k] = b[k2]; 
            else
                b[k] = -1;
            return;
        }
        
        if(b[k2]<0){
            if(b[k1]>=0)
                b[k] = b[k1]; 
            else
                b[k] = -1;
            return;
        }
        
        if(__________________________)  //填空
            b[k] = b[k1];
        else
            b[k] = b[k2];
    }
}


    請仔細分析流程,填寫缺失的代碼。

    通過瀏覽器提交答案,只填寫缺失的代碼,不要填寫已有代碼或其它說明語句等。

a[b[k1]] > a[b[k2]]

 

 

 

6 六角填數

標題:六角填數

    如圖【1.png】所示六角形中,填入1~12的數字。

    使得每條直線上的數字之和都相同。

    圖中,已經替你填好了3個數字,請你計算星號位置所代表的數字是多少?

請通過瀏覽器提交答案,不要填寫多余的內容。

10

 

public class Main {
    
    public boolean check(int[] A) {
        int[] B = new int[12];
        B[0] = 1;
        B[1] = 8;
        B[11] = 3;
        for(int i = 2;i <= 10;i++)
            B[i] = A[i - 2];
        int num = B[1] + B[2] + B[3] + B[4];
        if(B[0] + B[2] + B[5] + B[7] != num)
            return false;
        if(B[0] + B[3] + B[6] + B[10] != num)
            return false;
        if(B[7] + B[8] + B[9] + B[10] != num)
            return false;
        if(B[1] + B[5] + B[8] + B[11] != num)
            return false;
        if(B[4] + B[6] + B[9] + B[11] != num)
            return false;
        for(int i = 0;i < 12;i++)
            System.out.print(B[i]+" ");
        System.out.println();
        return true;
    }
    
    public void swap(int[] A, int a, int b) {
        int temp = A[a];
        A[a] = A[b];
        A[b] = temp;
    }
    
    public void dfs(int[] A, int step) {
        if(step == 9) {
            if(check(A))
                System.out.println(A[3]);
            return;
        } else {
            for(int i = step;i < 9;i++) {
                swap(A, i, step);
                dfs(A, step + 1);
                swap(A, i, step);
            }
        }
        return;
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        int[] A = {2,4,5,6,7,9,10,11,12};
        test.dfs(A, 0);
    }
}

 

 

 

7 繩圈

標題:繩圈

    今有 100 根繩子,當然會有 200 個繩頭。

    如果任意取繩頭兩兩配對,把所有繩頭都打結連接起來。最后會形成若干個繩圈(不考慮是否套在一起)。

    我們的問題是:請計算最后將形成多少個繩圈的概率最大?

    注意:結果是一個整數,請通過瀏覽器提交該數字。不要填寫多余的內容。

3


本題對數學的推理要求比較高,首先我們要分析,現在有n根繩子,那么任意選取其中兩個繩頭配對,直到所有繩頭均已完成配對,有多少種情況呢?

假設n-1根繩子配對完畢共有f(n - 1)種情,那么在此基礎上加一根繩子,重新進行配對,有以下兩種情況可以選擇:(1)繩圈個數不變,在n - 1根已配對完畢的繩頭中選擇一個繩頭和當前新添加的一根繩子繩頭打結連接起來;(2)增加一個繩圈,直接讓新添加的繩子兩個繩頭直接相連。

所以f(n) = f(n - 1) * (C(1, 2*(n - 1))+ C(0, 2*(n - 1))) = f(n - 1)  * (2*n - 2 + 1) = f(n - 1) * (2*n - 1)

有了上述公式,那么可以推導出解決本題的動態轉移方程,dp[i][j] = dp[i - 1][j] * (2*n - 2) / (2*n - 1) + dp[i - 1][j - 1] * (1) / (2*n - 1)

dp[i][j]表示當前有i根繩子,形成j個繩圈的概率。(PS:j > i,其概率為0)

 

public class Main {
    
    public static void main(String[] args) {
        double[][] dp = new double[101][101];
        dp[1][1] = 1;  //當前只有一根繩子,只能形成一個繩圈,且概率為1
        for(int i = 2;i < 101;i++) {  //繩子數
            for(int j = 1;j < 101;j++) {  //繩圈數
                if(j > i)  //此時的情形不可能出現,即此時概率為0
                    continue;
                dp[i][j] = dp[i - 1][j]*(2*i - 2) / (2*i - 1) + dp[i][j -1]/(2*i - 1);
            }
        }
        double max = 0;
        int maxI = 0;
        for(int i  = 1;i < 101;i++) {
            if(max < dp[100][i]) {
                max = dp[100][i];
                maxI = i;
            }
        }
        System.out.println(maxI);
    }
}

 

 

 

8 蘭頓螞蟻

標題:蘭頓螞蟻

    蘭頓螞蟻,是於1986年,由克里斯·蘭頓提出來的,屬於細胞自動機的一種。

    平面上的正方形格子被填上黑色或白色。在其中一格正方形內有一只“螞蟻”。
    螞蟻的頭部朝向為:上下左右其中一方。

    螞蟻的移動規則十分簡單:
    若螞蟻在黑格,右轉90度,將該格改為白格,並向前移一格;
    若螞蟻在白格,左轉90度,將該格改為黑格,並向前移一格。

    規則雖然簡單,螞蟻的行為卻十分復雜。剛剛開始時留下的路線都會有接近對稱,像是會重復,但不論起始狀態如何,螞蟻經過漫長的混亂活動后,會開辟出一條規則的“高速公路”。

    螞蟻的路線是很難事先預測的。

    你的任務是根據初始狀態,用計算機模擬蘭頓螞蟻在第n步行走后所處的位置。

【數據格式】

輸入數據的第一行是 m n 兩個整數(3 < m, n < 100),表示正方形格子的行數和列數。
接下來是 m 行數據。
每行數據為 n 個被空格分開的數字。0 表示白格,1 表示黑格。

接下來是一行數據:x y s k, 其中x y為整數,表示螞蟻所在行號和列號(行號從上到下增長,列號從左到右增長,都是從0開始編號)。s 是一個大寫字母,表示螞蟻頭的朝向,我們約定:上下左右分別用:UDLR表示。k 表示螞蟻走的步數。

輸出數據為兩個空格分開的整數 p q, 分別表示螞蟻在k步后,所處格子的行號和列號。


例如, 輸入:
5 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
2 3 L 5
程序應該輸出:
1 3

再例如, 輸入:
3 3
0 0 0
1 1 1
1 1 1
1 1 U 6
程序應該輸出:
0 0


資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗  < 1000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。
注意:不要使用package語句。不要使用jdk1.7及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效代碼處理。

 

import java.util.Scanner;

public class Main {
    public static int[][] move = {{-1,0},{1,0},{0,-1},{0,1}};//表示分別向上、下、左、右移動一步
    public static int[] left = {2,1,3,0}; //向左轉90度,其中2向左、1向下、3向右、0向上
    public static int[] right = {2,0,3,1}; //向右轉90度,具體數字含義同上
    
    public void bfs(int[][] value, int x, int y, String tempArea, int count) {
        int area = 0;
        if(tempArea.equals("L"))
            area = 2;
        else if(tempArea.equals("R"))
            area = 3;
        else if(tempArea.equals("U"))
            area = 0;
        else if(tempArea.equals("D"))
            area = 1;
        int step = 0;
        while(step < count) {
            if(value[x][y] == 1) {  //黑色格子,當前方向area要向右轉90度
                value[x][y] = 0;
                int i = 0;
                for(;i < 4;i++) {
                    if(right[i] == area)
                        break;
                }
                i = (i + 1) % 4;
                area = right[i];
                x = x + move[area][0];
                y = y + move[area][1];        
                step++;
            } else {   //白色格子,當前方向area要向左轉90度
                value[x][y] = 1;
                int i = 0;
                for(;i < 4;i++) {
                    if(left[i] == area)
                        break;
                }
                i = (i + 1) % 4;
                area = left[i];
                x = x + move[area][0];
                y = y + move[area][1];
                step++;
            }
        }
        System.out.println(x+" "+y);
        return;
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        int m = in.nextInt();
        int n = in.nextInt();
        int[][] value = new int[m][n];
        for(int i = 0;i < m;i++)
            for(int j = 0;j < n;j++)
                value[i][j] = in.nextInt();
        int x = in.nextInt();
        int y = in.nextInt();
        String tempArea = in.next();
        int count  = in.nextInt();
        test.bfs(value, x, y, tempArea, count);
    }
}

 

 

 

9 斐波那契

標題:斐波那契

    斐波那契數列大家都非常熟悉。它的定義是:

    f(x) = 1                    .... (x=1,2)
    f(x) = f(x-1) + f(x-2)      .... (x>2)

    對於給定的整數 n 和 m,我們希望求出:
    f(1) + f(2) + ... + f(n)  的值。但這個值可能非常大,所以我們把它對 f(m) 取模。
    公式參見【圖1.png】

    但這個數字依然很大,所以需要再對 p 求模。

【數據格式】
輸入為一行用空格分開的整數 n m p (0 < n, m, p < 10^18)
輸出為1個整數

例如,如果輸入:
2 3 5
程序應該輸出:
0

再例如,輸入:
15 11 29
程序應該輸出:
25

資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗  < 2000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。
注意:不要使用package語句。不要使用jdk1.7及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效代碼處理。


首先,關於斐波那契數的求取,如果使用遞歸法求取,會出現遠遠超時;迭代法求取差不多也會超時,此處,最好使用矩陣相乘法求取第n個斐波那契數。

其次,關於求取前n個斐波那契數和的問題,利用斐波那契數的性質(網上資料參考所得):S(n) = F(n+2) - 1,其中S(n)是前n個斐波那契數的和,F(n + 2)是第n+2個斐波那契數。

最后,要考慮n,m的取值問題,經過使用計算機運算檢測,一般n > 100,F(n)就會超過long型最大值,所以此處建議使用BigInteger類型,來存儲斐波那契數。

下面的代碼僅供參考,不保證n、m、p達到10^18數量級時,大整數類型的取余不會出現內存溢出問題哦。

 

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static BigInteger[][] ONE = {{BigInteger.ONE, BigInteger.ONE},
        {BigInteger.ONE,BigInteger.ZERO}};
    public static BigInteger[][] ZERO = {{BigInteger.ZERO,BigInteger.ZERO},
        {BigInteger.ZERO,BigInteger.ZERO}};
    //求取矩陣ONE的n次方
    public BigInteger[][] getOneOfN(long n) {
        if(n == 0)
            return ZERO;
        if(n == 1)
            return ONE;
        if((n & 1) == 0) {   //當n為偶數時
            BigInteger[][] A = getOneOfN(n >> 1);
            return multiMatrix(A, A);
        }
        //當n為奇數時
        BigInteger[][] A = getOneOfN(n >> 1);
        return multiMatrix(multiMatrix(A, A), ONE);
    }
    //求取矩陣A*B的值
    public BigInteger[][] multiMatrix(BigInteger[][] A, BigInteger[][] B) {
        BigInteger[][] result = new BigInteger[A.length][B[0].length];
        for(int i = 0;i < A.length;i++)
            for(int j = 0;j < B[0].length;j++)
                result[i][j] = BigInteger.ZERO;
        for(int i = 0;i < A.length;i++)
            for(int j = 0;j < B.length;j++)
                for(int k = 0;k < A[0].length;k++)
                    result[i][j] = result[i][j].add(A[i][k].multiply(B[k][j]));
        return result;
    }
    //獲取第n個斐波那契數
    public BigInteger getFibonacci(long n) {
        if(n == 1 || n == 2)
            return BigInteger.ONE;
        BigInteger[][] A  = new BigInteger[1][2];
        A[0][0] = BigInteger.ONE;
        A[0][1] = BigInteger.ONE;
        BigInteger[][] B = getOneOfN(n - 2);
        A = multiMatrix(A, B);
        return A[0][0];
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        long n = in.nextLong();
        long m = in.nextLong();
        BigInteger p = in.nextBigInteger();
        BigInteger result = BigInteger.ZERO;
        result = test.getFibonacci(n + 2).subtract(BigInteger.ONE);
        result = result.mod(test.getFibonacci(m));
        result = result.mod(p);
        System.out.println(result);
    }
    
}

 

 

 

10 波動數列

標題:波動數列

    觀察這個數列:
    1 3 0 2 -1 1 -2 ...

    這個數列中后一項總是比前一項增加2或者減少3。

    棟棟對這種數列很好奇,他想知道長度為 n 和為 s 而且后一項總是比前一項增加a或者減少b的整數數列可能有多少種呢?

【數據格式】
    輸入的第一行包含四個整數 n s a b,含義如前面說述。
    輸出一行,包含一個整數,表示滿足條件的方案數。由於這個數很大,請輸出方案數除以100000007的余數。

例如,輸入:
4 10 2 3
程序應該輸出:
2

【樣例說明】
這兩個數列分別是2 4 1 3和7 4 1 -2。

【數據規模與約定】
對於10%的數據,1<=n<=5,0<=s<=5,1<=a,b<=5;
對於30%的數據,1<=n<=30,0<=s<=30,1<=a,b<=30;
對於50%的數據,1<=n<=50,0<=s<=50,1<=a,b<=50;
對於70%的數據,1<=n<=100,0<=s<=500,1<=a, b<=50;
對於100%的數據,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。

資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗  < 2000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。
注意:不要使用package語句。不要使用jdk1.7及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效代碼處理。

 

import java.util.Scanner;

public class Main {
    public static int count = 0;
    public static int n = 0;
    public static int s = 0;
    public static int a = 0;
    public static int b = 0;
    public static int sum = 0;
    
    public void dfs(int i, int step) {
        if(step == n) {
            if(sum == s)
                count = (count + 1) % 100000007;
            return;
        }
        sum += i;
        dfs(i + a, step + 1);
        dfs(i - b, step + 1);
        sum -= i;
        return;
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        s = in.nextInt();
        a = in.nextInt();
        b = in.nextInt();
        for(int i=s-n*b;i<s+n*a;i++)
            test.dfs(i, 1);
        System.out.println(count);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM