1. 程式人生 > 其它 >【備考06組01號】第四屆藍橋杯JAVA組A組國賽題解

【備考06組01號】第四屆藍橋杯JAVA組A組國賽題解

本部落格為所有部落格的彙總頁, 將長期更新,任何改進建議,歡迎在評論區留言, 你們的三連是我最大的動力,你們敢投,我就敢肝

1.填算式

(1)題目描述

請看下面的算式:

(ABCD - EFGH) * XY = 900

每個字母代表一個0~9的數字,不同字母代表不同數字,首位不能為0。

比如,(5012 - 4987) * 36 就是一個解。

請找到另一個解,並提交該解中 ABCD 所代表的整數。

請嚴格按照格式,通過瀏覽器提交答案。
注意:只提交 ABCD 所代表的整數,不要寫其它附加內容,比如:說明性的文字。

(2)涉及知識點:dfs全排列+簡單計算
(3)分析與解答:這道題目沒什麼難度,會全排列肯定能做出來,列舉所有的可能性,然後將三個數字分別表示出來,由於全排列的關係,所以無所謂那個數字做什麼位,就按照順序加就可以了。


(4)程式碼:

點選檢視程式碼
    public class Main04JA01 {
    
        /**
         * @param args
         */
        public static int a[]={0,1,2,3,4,5,6,7,8,9};
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            dfs(a,0);
        }
        public static void dfs(int a[],int k){
            if(k==10){
                if(check()){
                    System.out.print(a[0]*1000+a[1]*100+a[2]*10+a[3]);
                    System.out.println();
                }
            }else{
                for(int i=k;i<10;i++){
                    int temp=a[i];
                    a[i]=a[k];
                    a[k]=temp;
                    dfs(a,k+1);
                    temp=a[i];
                    a[i]=a[k];
                    a[k]=temp;
                }
            }
        }
        private static boolean check() {
            // TODO Auto-generated method stub
            int res1=a[0]*1000+a[1]*100+a[2]*10+a[3];
            int res2=a[4]*1000+a[5]*100+a[6]*10+a[7];
            int res3=a[8]*10+a[9];
            return ((res1-res2)*res3)==900&&a[0]!=0&&a[4]!=0&&a[8]!=0;
        }
    }

2.骰子迷題

(1)題目描述

小明參加了少年宮的一項趣味活動:每個小朋友發給一個空白的骰子(它的6個面是空白的,沒有數字),要小朋友自己設計每個面寫哪個數字。但有如下要求:

1. 每個面只能填寫 0 至 8 中的某一個數字。

2. 不同面可以填寫同樣的數字,但6個面總和必須等於24。

填好後,小朋友可以用自己填寫好數字的骰子向少年宮的兩個機器人挑戰----玩擲骰子游戲。規則如下:

三方同時擲出自己的骰子,如果出現任何相同的數字,則三方都不計分。

如果三方數字都不同,則最小數字一方扣 1 分,最大數字一方加 1 分。

小明看到了兩個機器人手中的骰子分別是:

0 0 0 8 8 8

1 1 4 5 6 7

請你替小明算一下,他如何填寫,才能使自己得分的概率最大。

請提交小明應該填寫的6個數字,按升序排列,數字間用一個空格分開。

如果認為有多個答案,提交字母序最小的那個方案。

請嚴格按照格式,通過瀏覽器提交答案。
注意:只提交一行內容,含有6個被空格分開的數字。不要寫其它附加內容,比如:說明性的文字。

(2)涉及知識點:dfs+概率運算

(3)分析與解答:這道題我在網上看到有剪枝,其實這道題沒有必要,因為資料量很小,即使是六層迴圈列舉所有情況也非常小,所以直接搜尋就可以了,6層迴圈我還沒有試過,考場上沒方法的時候不妨嘗試一下,反正不會超時。這道題的思路是什麼呢,其實很簡單,首先列舉,然後只要符合條件的再把比兩個機器人數字都大的情況比較出來最後再相乘就可以了,這裡其實兩個數字應該是要分別除以6的,但是因為不需要求最大的概率結果,所以不需要給自己增加難度。

(4)程式碼:

點選檢視程式碼
 public class Main04JA02 {
    
        /**
         * @param args
         */
         static int a[]={0,0,0,8,8,8};
         static int b[]={1,1,4,5,6,7};
         static int c[]=new int[6];
         static int res[]=new int[6];
         static int max=0;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            dfs(0,0,0);
            for(int i=0;i<6;i++){
                System.out.print(res[i]+" ");
            }
            System.out.println();
        }
        public static void dfs(int n,int cur,int sum){
            if(sum>24){
                return;
            }
            if(n==6){
                if(sum==24){
                    count();
                }
                return;
            }
            for(int i=cur;i<9;i++){
                c[n]=i;
                dfs(n+1,i,sum+i);
                c[n]=0;
            }
        }
        private static void count() {
            // TODO Auto-generated method stub
            int m=0;int x1=0;int x2=0;
            for(int i=0;i<6;i++){
                x1=x2=0;
                for(int j=0;j<6;j++){
                    if(c[i]>a[j]){
                        x1++;
                    }
                }
                for(int j=0;j<6;j++){
                    if(c[i]>b[j]){
                        x2++;
                    }
                }
                m+=x1*x2;
            }
            if(m>max){
                for(int i=0;i<6;i++){
                    res[i]=c[i];
                }
                max=m;
            }
        }
    }

3.埃及分數

(1)題目描述

古埃及曾經創造出燦爛的人類文明,他們的分數表示卻很令人不解。古埃及喜歡把一個分數分解為類似: 1/a + 1/b 的格式。

這裡,a 和 b 必須是不同的兩個整數,分子必須為 1

比如,2/15 一共有 4 種不同的分解法(姑且稱為埃及分解法):

1/8 + 1/120
1/9 + 1/45
1/10 + 1/30
1/12 + 1/20


那麼, 2/45 一共有多少個不同的埃及分解呢(滿足加法交換律的算同種分解)? 請直接提交該整數(千萬不要提交詳細的分解式!)。

請嚴格按照要求,通過瀏覽器提交答案。
注意:只提交分解的種類數,不要寫其它附加內容,比如:說明性的文字

(2)涉及知識點:雙精度運算+暴力列舉

(3)分析與解答:這道題乍看之下非常簡單,但是國賽遇到這種題目千萬長個心眼,後面可能暗藏著什麼陷阱,其實你想想看也知道,國賽A組第三題,怎麼可能出這種初學者都會做的題目,這道題目出得非常刁鑽。刁鑽在哪裡,一開始我用的是(double)1/i+(double)1/j==(double)2/45,為了保險我還特地測了一下2/15的答案,結果2/15的答案還湊巧是對的,但是這道題目結果換成2/45就是錯的,其實具體原因我也說不清楚,我唯一的理解就是double只有14位小數,運算算不到那麼精準吧,當然我也不會誤差排除,所以這裡換個方法。怎麼辦呢?其實除是不精確的,但是乘法是一定精確的,所以在個人建議能用乘法儘量不要用除法,所以這裡要做的是同分,轉換成乘法45*j+45*i=2*i*j,其實這樣double型別都不需要了,int型就可以了。

(4)程式碼:

點選檢視程式碼
    public class Main04JA03 {
    
        private static int ans;
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            for(double i=1;i<=10000;i++){
                for(double j=i+1;j<=10000;j++){
                    if((double)45*j+45*i==(double)2*i*j){
                        System.out.println(i+" "+j);
                        ans++;
                    }
                }
            }
            System.out.println(ans);
        }
    
    }

4.約數倍數選卡片

(1)題目描述

閒暇時,福爾摩斯和華生玩一個遊戲:

在N張卡片上寫有N個整數。兩人輪流拿走一張卡片。要求下一個人拿的數字一定是前一個人拿的數字的約數或倍數。例如,某次福爾摩斯拿走的卡片上寫著數字“6”,則接下來華生可以拿的數字包括:

1,2,3, 6,12,18,24 ....

當輪到某一方拿卡片時,沒有滿足要求的卡片可選,則該方為輸方。

請你利用計算機的優勢計算一下,在已知所有卡片上的數字和可選哪些數字的條件下,怎樣選擇才能保證必勝!

當選多個數字都可以必勝時,輸出其中最小的數字。如果無論如何都會輸,則輸出-1。


輸入資料為2行。第一行是若干空格分開的整數(每個整數介於1~100間),表示當前剩餘的所有卡片。
第二行也是若干空格分開的整數,表示可以選的數字。當然,第二行的數字必須完全包含在第一行的數字中。

程式則輸出必勝的招法!!


例如:
使用者輸入:
2 3 6
3 6
則程式應該輸出:
3

再如:
使用者輸入:
1 2 2 3 3 4 5
3 4 5
則程式應該輸出:
4


資源約定:
峰值記憶體消耗(含虛擬機器) < 64M
CPU消耗 < 2000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意:不要使用package語句。不要使用jdk1.6及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效程式碼處理。

(2)涉及知識點:博弈論+dfs

(3)分析與解答:博弈論的題目我不太會做,畢竟沒系統學過,只能借人家的程式碼來講講思路了,這道題的博弈論思路大概是這樣,我們回過頭來看看輸入,輸入資料為2行。第一行是若干空格分開的整數(每個整數介於1~100間),表示當前剩餘的所有卡片。第二行也是若干空格分開的整數,表示可以選的數字。當然,第二行的數字必須完全包含在第一行的數字中。 程式則輸出必勝的招法!!
那麼上來沒思路怎麼辦呢?確認知識點以後,沒思路就從回到題目,一步步分析題目要我們幹什麼,很多參賽選手有個通病,就是往往會對題目想當然,所以題目中的細節會忽略掉,這次省賽就是很好的教訓,我們可以看到題目中有一句話是當輪到某一方拿卡片時,沒有滿足要求的卡片可選,則該方為輸方。而且又不是每種牌只有一張,所以可以很清楚地確認一定是用陣列下標記錄數量,第二行是後續的方案,要我們選擇肯定能贏的方案,這裡能夠聯想到什麼呢?勇往直前全部試一遍最後看結果,是什麼演算法,那只有dfs。
抓住知識點以後,後面就要開始設計演算法,dfs要想到並不難,難的是要去搜什麼,我每次想到dfs但是都想不到去搜什麼,這就很絕望了,所以要抓住題目的意思呀,國賽的時間應該是夠的,四個小時,前三題一般花不了多少時間的,後面兩三個小時做兩題還是很足夠的,所以冷靜點思考,上機考考的不僅僅是解題能力,還有心理素質,最後一題如果可以的話暴力騙分就行了,這樣國一肯定沒問題了,當然啦大佬除外。扯遠了哈,dfs要去搜什麼呢,是不是搜一個肯定能贏的方案,那麼哪裡去找所有的方案呢,那麼我們就建立一個連結串列陣列,儲存所有的方案就可以了,這裡一定要注意放回去,不然的話不能列舉所有的方案,最後再根據我拿的牌搜尋所有的方案,直到所有的牌都拿完,這裡就不用放回去了哈哈,不然玩到死都玩不完了。

(4)程式碼:

點選檢視程式碼
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Scanner;
    
    public class Main04JA04 {
    
        /**
         * @param args
         */
        static int[]num=new int[105];
        static ArrayList<Integer>g[]=new ArrayList[105];
        static ArrayList<Integer>choice=new ArrayList<>();
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Scanner scan=new Scanner(System.in);
            String[]s1=scan.nextLine().split(" ");
            String[]s2=scan.nextLine().split(" ");
            for(int i=0;i<=100;i++){
                g[i]=new ArrayList<>();
            }
            for(String s:s1){
                if(!s.equals("")){
                    num[Integer.parseInt(s)]++;
                }
            }
            for(String s:s2){
                if(!s.equals("")){
                    choice.add(Integer.parseInt(s));
                }
            }
            for(int i=1;i<=100;i++){
                if(num[i]>0){
                    num[i]--;
                    for(int j=1;j<=100;j++){
                        if(num[j]>0&&(i%j==0||j%i==0)){
                            g[i].add(j);
                        }
                    }
                    num[i]++;
                }
            }
            
            int flag=-1;
            Collections.sort(choice);
            for(int x:choice){
                num[x]--;
                if(dfs(x)==1){
                    flag=x;
                    break;
                }
                num[x]++;
            }
            System.out.println(flag);
        }
        private static int dfs(int u) {
            // TODO Auto-generated method stub
            for(int i=g[u].size()-1;i>=0;i--){
                int v=g[u].get(i);
                if(num[v]>0){
                    num[v]--;
                    int win=dfs(v);
                    num[v]++;
                    if(win==1){
                        return -1;
                    }
                }
            }
            return 1;
        }
    
    }

5.網路尋路

(1)題目描述

X 國的一個網路使用若干條線路連線若干個節點。節點間的通訊是雙向的。某重要資料包,為了安全起見,必須恰好被轉發兩次到達目的地。該包可能在任意一個節點產生,我們需要知道該網路中一共有多少種不同的轉發路徑。

源地址和目標地址可以相同,但中間節點必須不同。

如圖1所示的網路。

1 -> 2 -> 3 -> 1 是允許的

1 -> 2 -> 1-> 2 或者 1->2->3->2 都是非法的。

輸入資料的第一行為兩個整數N M,分別表示節點個數和連線線路的條數(1<=N<=10000; 0<=M<=100000)。
接下去有M行,每行為兩個整數 u 和 v,表示節點u 和 v 聯通(1<=u,v<=N , u!=v)。

輸入資料保證任意兩點最多隻有一條邊連線,並且沒有自己連自己的邊,即不存在重邊和自環。


輸出一個整數,表示滿足要求的路徑條數。

例如:
使用者輸入:
3 3
1 2
2 3
1 3
則程式應該輸出:
6

(2)涉及知識點:無向圖構建+dfs

(3)分析與解答:今年的題目是搜尋專題吧,我估計是第四屆出題還不熟練,總共6道題4道題考DFS,1道題考雙精度運算,1道題考數論,可見DFS在藍橋杯國賽中還是很重要的,所以必須要熟練掌握才行。這道題其實比上一道題思路簡單,只要構建好無向圖,對每個點搜尋一下就行了,而且這道題目最簡單的就是路線是固定死的,只能是兩次,那麼走三步就可以了,中間不能經過走過的點,也不能回到起點再來一次,那麼用訪問標記陣列就行了,加上沒有重邊和自環還是很簡單的。

(4)程式碼:

    import java.util.ArrayList;
    import java.util.Scanner;
    
    public class Main04JA05 {
        static int n,m,ans=0;
        static ArrayList<Integer>[]g;
        static boolean[]vis;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Scanner scan=new Scanner(System.in);
            n=scan.nextInt();
            m=scan.nextInt();
            g=new ArrayList[n+5];
            vis=new boolean[n+5];
            for(int i=0;i<=n;i++){
                g[i]=new ArrayList<>();
            }
            for(int i=1;i<=m;i++){
                int a=scan.nextInt();
                int b=scan.nextInt();
                g[a].add(b);
                g[b].add(a);
            }
            for(int i=1;i<=n;i++){
                dfs(i,-1,3);
            }
            System.out.println(ans);
            
        }
        private static void dfs(int u, int fa, int step) {
            // TODO Auto-generated method stub
            if(step==0){
                ans++;
                return;
            }
            for(int v:g[u]){
                if(!vis[v]&&v!=fa){
                    vis[v]=true;
                    dfs(v,u,step-1);
                    vis[v]=false;
                }
            }
        }
    
    }

6.公式求值

(1)題目描述

輸入n, m, k,輸出圖1所示的公式的值。其中C_n^m是組合數,表示在n個人的集合中選出m個人組成一個集合的方案數。組合數的計算公式如圖2所示。

輸入的第一行包含一個整數n;第二行包含一個整數m,第三行包含一個整數k。

計算圖1所示的公式的值,由於答案非常大,請輸出這個值除以999101的餘數。

【樣例輸入1】
3
1
3
【樣例輸出1】
162

【樣例輸入2】
20
10
10
【樣例輸出2】
359316

【資料規模與約定】
對於10%的資料,n≤10,k≤3;
對於20%的資料,n≤20,k≤3;
對於30%的資料,n≤1000,k≤5;
對於40%的資料,n≤10^7,k≤10;
對於60%的資料,n≤10^15,k ≤100;
對於70%的資料,n≤10^100,k≤200;
對於80%的資料,n≤10^500,k ≤500;
對於100%的資料,n在十進位制下不超過1000位,即1≤n<10^1000,1≤k≤1000,同時0≤m≤n,k≤n。

【提示】
999101是一個質數;
當n位數比較多時,絕大多數情況下答案都是0,但評測的時候會選取一些答案不是0的資料;

資源約定:
峰值記憶體消耗(含虛擬機器) < 128M
CPU消耗 < 2000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。
注意:不要使用package語句。不要使用jdk1.6及以上版本的特性。
注意:主類的名字必須是:Main,否則按無效程式碼處理。


(2)涉及知識點:數論相關知識

(3)分析與解答:這道題我一時半會兒看不懂,畢竟數論沒怎麼學,附上大佬程式碼(90%的通過率)

https://blog.csdn.net/u010836847/article/details/21166725?utm_source=copy

(4)程式碼:

點選檢視程式碼
   import java.math.BigInteger;
    import java.util.Scanner;
     
    public class Main04JA06 {
        /***
         * @author 林梵
         */
        public static BigInteger lucas(BigInteger n,BigInteger m,BigInteger p){
            if(m.equals(BigInteger.ZERO)) return BigInteger.ONE;
            return BigInteger.valueOf(f(n.mod(p).longValue(),m.mod(p).longValue())).multiply(lucas(n.divide(p),m.divide(p),p)).mod(p);
        }
     
        public static long f(long n,long m){
            if(m>n) return 1;
            if(n==m|| m==0) return 1;
            if(m>n-m) m=n-m;
            long tmpi=1,tmpn=1,s1=1,s2=1,ans=1;
            for (int i = 1; i<=m; i++) {
                tmpi=i;
                tmpn=n-i+1;
                s1=s1*tmpi%999101;
                s2=s2*tmpn%999101;
            }
            ans = s2*pow1(s1,999099)%999101;
            return ans%999101;
        }
        public static long pow1(long x,long n) {
            if(x==1) return 1;
            if (n==0)
                return 1;
            else {
                while ((n & 1)==0) {
                    n>>=1;
                    x=(x *x)%999101;
                }
            }
            long result = x%999101;
            n>>=1;
            while (n!=0) {
                x=(x *x)%999101;;
                if ((n & 1)!=0)
                    result =result*x%999101;
                n>>=1;
            }
            return  result;
        }
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            BigInteger n = new BigInteger(sc.nextLine());
            BigInteger m = new BigInteger(sc.nextLine());
            int k = Integer.parseInt(sc.nextLine());
            long start = System.currentTimeMillis();
            BigInteger md = new BigInteger("999101");
            long Cnm=lucas(n, m,md).longValue()%999101;
            long sum = 0;
            if(Cnm!=0){
                int[][] a = new int[k][k];
                int h = 1;
                for (int i = 0; i < k; i++) {
                    for (int j = 0; j < k; j++) {
                        if (j >= h)
                            a[i][j] =0;
                        else {
                            if (j == 0 || j == h - 1)
                                a[i][j] = 1;
                            else {
                                a[i][j] = (a[i - 1][j - 1]*(h - j)+a[i - 1][j])%999101;
                            }
                        }
                    }
                    h++;
                }
                long m1 = 1,n1 =1;
                long x=n.subtract(new BigInteger(k+"")).mod(md.subtract(BigInteger.ONE)).longValue();
                long n3 = pow1(2,x);
                for (int i = k - 1; i >= 0; i--) {
                    n1=n3*pow1(2,i)%999101;
                    m1 = m1*(n.subtract(new BigInteger((k - 1 - i) + "")).mod(md).longValue())%999101;
                    sum = (sum+m1*a[k - 1][i]*n1)%999101;
                }
                sum = sum*Cnm%999101;
            }
            System.out.println(sum);
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
     
    } 
在黑夜裡夢想著光,心中覆蓋悲傷,在悲傷裡忍受孤獨,空守一絲溫暖。 我的淚水是無底深海,對你的愛已無言,相信無盡的力量,那是真愛永在。 我的信仰是無底深海,澎湃著心中火焰,燃燒無盡的力量,那是忠誠永在