蓝桥杯第五届国赛javab组题目解析

第一题:

 


标题:国王的遗产

    X国是个小国。国王K有6个儿子。在临终前,K国王立下遗嘱:国王的一批牛作为遗产要分给他的6个儿子。
    其中,大儿子分1/4,二儿子1/5,三儿子1/6,....
    直到小儿子分1/9。
    牛是活的,不能把一头牛切开分。

    最后还剩下11头牛,分给管家。

    请计算国王这批遗产中一共有多少头牛。

    这是一个整数,请通过浏览器提交答案,不要填写任何多余的内容(比如说明性的文字)

答案:2520

 

第二题:


标题:六角幻方

    把 1 2 3 ... 19 共19个整数排列成六角形状,如下:

    * * *
   * * * *
  * * * * *
   * * * * 
    * * *

    要求每个直线上的数字之和必须相等。共有15条直线哦!

    再给点线索吧!我们预先填好了2个数字,第一行的头两个数字是:15 13,参见图【p1.png】,黄色一行为所求。

    请你填写出中间一行的5个数字。数字间用空格分开。

    这是一行用空格分开的整数,请通过浏览器提交答案,不要填写任何多余的内容(比如说明性的文字等)
 


public class Main{
	static int[][] ab={{2,6,11},{0,3,7},{7,12,16},{11,15,18},{16,17,18},{3,4,5,6},{7,8,9,10,11},{12,13,14,15},{1,4,8,12},{2,5,9,13,16},{6,10,14,17},{1,5,10,15},{0,4,9,14,18},{3,8,13,17}};
	//构建所有的直线
	static int[] arr;//arr为当前的图案每个单元数
	static boolean[] bo;//bo为记录每个数是否用过
	public static void main(String[] args)throws Exception{
//		long t1=System.currentTimeMillis();
		arr=new int[19];
		arr[0]=15;
		arr[1]=13;
		arr[2]=10;
		bo=new boolean[20];
		bo[10]=true;
		bo[13]=true;
		bo[15]=true;
		p(0);
		long t2=System.currentTimeMillis();
//		System.out.println(t2-t1);//2ms
	}
	public static void p(int pi) {//这个方法是递归每个直线
		if(pi==ab.length) {
			System.out.println(arr[7]+" "+arr[8]+" "+arr[9]+" "+arr[10]+" "+arr[11]);
//			System.out.println(arr[0]+" "+arr[1]+" "+arr[2]);
//			System.out.println(arr[3]+" "+arr[4]+" "+arr[5]+" "+arr[6]);
//			System.out.println(arr[7]+" "+arr[8]+" "+arr[9]+" "+arr[10]+" "+arr[11]);
//			System.out.println(arr[12]+" "+arr[13]+" "+arr[14]+" "+arr[15]);
//			System.out.println(arr[16]+" "+arr[17]+" "+arr[18]);
			return;
		}
		pp(pi,0);
	}
	public static void pp(int pi,int pj) {//这个方法是对每个直线的每个单元进行填数和判断
		if(pj==ab[pi].length) {//如果把这一条线填完了,就进行判断,如果可行,那就进行下一条线的判断
			int sum=0;
			for(int i=0;i

这道题显然是典型的建立公式题目(因为暴力的运转是15!次也就是10^13,相当于运行几千小时,显然不可能得到答案的),建立一个固定公式,对每个直线,用类似做数独的方式进行填数——也就是数独的固定最简方法,从可能性最少的线开始填数,一直到可能性最大的直线——也就是程序中的ab数组顺序。

数独方式的算法不存在超时——因为几乎不存在运行运行时间——这个程序的运行时间是2ms。(暴力居然会超时几千小时?哼哼,看我不把你优化到臭虫级别我就不放过你!)

 

第三题:

 

 


标题:格子放鸡蛋

    X星球的母鸡很聪明。它们把蛋直接下在一个 N * N 的格子中,每个格子只能容纳一枚鸡蛋。它们有个习惯,要求:每行,每列,以及每个斜线上都不能有超过2个鸡蛋。如果要满足这些要求,母鸡最多能下多少蛋呢,有多少种摆放方法呢?  

    下面的程序解决了这个问题,请仔细分析程序逻辑,推断划线处缺少的代码。

public class A
{
    static int max = 0;
    static int T = 0;
    static final int N = 6;
        
    static void f(int[][] da, int r, int c)
    {    
        if(r>=N){
            int n = count(da);
            if(n>max) {
                max = n;
                T = 0;
            }
            if(n==max) T++;
            return;
        }
        
        int r_next = r;
        int c_next = c + 1;
        if(c_next>=N){
            c_next = 0;
            r_next++;
        }
        
        if(____________________){  // 填空位置
            da[r][c] = 1;
            f(da, r_next, c_next); 
        }
        
        da[r][c] = 0;
        f(da, r_next, c_next);
    }
    
    static int count(int[][] da)
    {
        int n = 0;
        
        for(int i=0; i         for(int j=0; j             if(da[i][j]==1) n++;
            
        return n;
    }
    
    static int spy(int[][] da, int r, int c)
    {
        int m=0;
        
        // 该行
        int n=0;
        for(int i=0; i         if(n>m) m = n;

        //该列
        n=0;
        for(int i=0; i         if(n>m) m = n;
        
        //右斜线
        n=0;
        for(int i=0; i             if(r-i<0 || c-i<0) break; 
            if(da[r-i][c-i]==1) n++;
        }
        for(int i=1; i             if(r+i>=N || c+i>=N) break; 
            if(da[r+i][c+i]==1) n++;
        }
        if(n>m) m = n;
        
        //左斜线
        n=0;
        for(int i=0; i             if(r-i<0 || c+i>=N) break; 
            if(da[r-i][c+i]==1) n++;
        }
        for(int i=1; i             if(r+i>=N || c-i<0) break; 
            if(da[r+i][c-i]==1) n++;
        }
        if(n > m) m = n;
        
        return m;
    }
    
    public static void main(String[] args)
    {
        int[][] da = new int[N][N];
        
        f(da, 0, 0);
        
        System.out.println(max);
        System.out.println(T);
    }
}

注意:通过浏览器提交答案。只填写缺少的内容,不要填写任何多余的内容(例如:说明性文字或已有符号)。

 

public class Main
{
	static int max = 0;
	static int T = 0;
	static final int N = 6;
		
	static void f(int[][] da, int r, int c)
	{	
		if(r>=N){//如果递归完毕,计算有多少个蛋
			int n = count(da);
			if(n>max) {//如果有更小的蛋,计数T归零,max更新
				max = n;
				T = 0;
			}
			if(n==max) T++;//否则计数加一
			return;
		}
		

		int r_next = r;
		int c_next = c + 1;
		if(c_next>=N){//往右走一步,如果走到底了,行数加一,列数归零
			c_next = 0;
			r_next++;
		}
		
		if(r_next>=N?false:spy(da,r_next,c_next)<2){  //如果当前行步数最后一行,那么计算这个地方是否可以放蛋
			da[r][c] = 1;
			f(da, r_next, c_next); 
		}
		
		da[r][c] = 0;
		f(da, r_next, c_next);
	}
	
	static int count(int[][] da)//计算有多少个蛋
	{
		int n = 0;
		
		for(int i=0; im) m = n;

		//该列
		n=0;
		for(int i=0; im) m = n;
		
		//右斜线
		n=0;
		for(int i=0; i=N || c+i>=N) break; 
			if(da[r+i][c+i]==1) n++;
		}
		if(n>m) m = n;
		
		//左斜线
		n=0;
		for(int i=0; i=N) break; 
			if(da[r-i][c+i]==1) n++;
		}
		for(int i=1; i=N || c-i<0) break; 
			if(da[r+i][c-i]==1) n++;
		}
		if(n > m) m = n;
		
		return m;
	}
	
	public static void main(String[] args)
	{
		int[][] da = new int[N][N];
		
		f(da, 0, 0);
		
		System.out.println(max);
		System.out.println(T);
	}
}

很简单的一个填空题,也就是考察spy的用途而已。

 

第四题:

 


标题:排列序数

   如果用a b c d这4个字母组成一个串,有4!=24种,如果把它们排个序,每个串都对应一个序号:
  abcd  0
  abdc  1
  acbd  2
  acdb  3
  adbc  4
  adcb  5
  bacd  6
  badc  7
  bcad  8
  bcda  9
  bdac  10
  bdca  11
  cabd  12
  cadb  13
  cbad  14
  cbda  15
  cdab  16
  cdba  17
  ...

    现在有不多于10个两两不同的小写字母,给出它们组成的串,你能求出该串在所有排列中的序号吗?

【输入格式】
一行,一个串。

【输出格式】
一行,一个整数,表示该串在其字母所有排列生成的串中的序号。注意:最小的序号是0。

例如:
输入:
bdca

程序应该输出:
11

再例如:
输入:
cedab

程序应该输出:
70


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


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

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


 

import java.util.Scanner;

public class Main{
	static int answ=0;
	public static void main(String[] args){
		Scanner in=new Scanner(System.in);
		char[] arr=in.next().toCharArray();
		p(arr,0);
		System.out.println(answ);
	}
	public static void p(char[] arr,int pi) {
		if(pi==arr.length)//如果到了最后一位结束循环
			return;
		int sum=1;
		for(int i=1;i

 

解析:

70=4*3*2*2+3*2*3+2*2+1*0

11=3*2*1+2*2+1*1

 

第五题:

 


标题:幂一矩阵

    天才少年的邻居 atm 最近学习了线性代数相关的理论,他对“矩阵”这个概念特别感兴趣。矩阵中有个概念叫做幂零矩阵。对于一个方阵 M ,如果存在一个正整数 k 满足 M^k = 0 ,那么 M 就是一个幂零矩阵。(^ 表示乘方)

    atm 不满足幂零矩阵,他自己设想了一个幂一矩阵:对于一个方阵 M ,如果存在一个正整数 k 满足 M^k = I ,其中 I 是单位矩阵,那么 M 就是一个幂一矩阵。

    atm 特别钟情于这样一种方阵:每行每列有且仅有一个 1 。经过 atm 不断实验,他发现这种矩阵都是幂一矩阵。

    现在,他的问题是,给定一个满足以上条件的方阵,他想求最小的 k 是多少。

【输入格式】
第一行一个正整数 n ,表示矩阵大小是 n * n 。
接下来 n 行,每行两个正整数 i j 表示方阵的第 i 行第 j 列为 1。
1 <= i, j <= n 。
行号,列号都从1开始。

【输出格式】
一行。一个正整数,即题目中所说最小的 k 。

【样例输入】
5
3 1
1 2
4 4
2 3
5 5

【样例输出】
3

【数据范围】
对于 30% 的数据满足 n <= 10 
对于 60% 的数据答案不超过 10^18 
对于 100% 的数据满足 n <= 10000


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


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

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


 

public class Main{
	public static void main(String[] args){
		Scanner in=new Scanner(System.in);
		int n=in.nextInt();
		int[] arr=new int[n];
		for(int i=0;i

 

解析:

技巧一:

如:某个矩阵里的某个“1”的位置是 (1,3),当它遇到(乘以)位置为(3,5)这样的(以3开头的坐标的)“1”,“1”的坐标就变成了(1,5),因此当它继续和位置x轴为5的“1”相遇,就会变成坐标为(1,x)的“1”,当x为1的时候,(1,1)的位置就是“1”了——而这个相遇(相乘)次数就是(1,1)变成1需要的最小次幂。  而每当数据的幂是这个最小循环次数的整数倍的时候,(1,1)这个位置就会是“1”。

 

技巧二:

我们就可以通过求出:每个(x,x)这样的坐标的数字为“1”的时候,就是这个最小的幂,也就是答案了。

因此我们就需要求每个(x,x)坐标变成“1”的时候需要的最小(相遇)相乘次数,把这些所有的次数gcd得到它的最小公倍数,这个最小公倍数就是这道题的答案。

 

技巧三:

而,在我们求(1,1)这个位置需求的最小相遇次数的时候,我们会遇到

(1,3)—>(3,5)=(1,5)

(1,5)—>(5,7)=(1,7)

(1,7)—>(7,1)=(1,1)

类似于这样的情况,

仔细观察会发现,如果得出1的最小循环次数为3的情况下,那么它相遇过的数5,7的最小循环次数也是3,且它只要相遇了的数字,就处于同一轮回之中,就肯定是相同的循环次数,至于如果实在不理解原因,可以想想如果不是相同的循环次数的情况下,自然就会出现,最终没办法达成单位矩阵了,因为根本没办法消掉这个“1”——于是根据这个情况,我们就可以大大减少重复计算的次数了。

 

技巧四:题中所说70%答案不会超过10^18,也就是说30%的答案超过了10^18次方,也就是说这些答案,long类型也没办法存储,自然就需要biginteger来进行计算答案了,而biginteger的方法里,查api可得:自带了gcd方法来求最大公约数也就可以求出最小公倍数了,那就是这道题的答案了。

 

第六题:


标题:供水设施

    X星球的居民点很多。Pear决定修建一个浩大的水利工程,以解决他管辖的N个居民点的供水问题。现在一共有N个水塔,同时也有N个居民点,居民点在北侧从1号到N号自西向东排成一排;水塔在南侧也从1号到N号自西向东排成一排。

    N条单向输水线(有水泵动力),将水从南侧的水塔引到北侧对应的居民点。

    我们不妨将居民点和水塔都看做平面上的点,居民点坐标为(1,K)~(N,K),水塔为(1,0)~(N,0)。

    除了N条纵向输水线以外,还有M条单向的横向输水线,连接(Xi,Yi)和(Xi,(Yi)+1)或者(Xi,Yi)和(Xi,(Yi)-1)。前者被称为向右的水路,而后者是向左的。不会有两条水路重叠,即便它们方向不同。

    布局的示意图如:【p1.png】所示。

    显然,每个水塔的水都可以到达若干个居民点(而不仅仅是对应的那个)。例如上图中,4号水塔可以到达3、4、5、6四个居民点。

    现在Pear决定在此基础上,再修建一条横向单项输水线。为了方便考虑,Pear认为这条水路应当是自左向右的,也就是连接了一个点和它右侧的点(例如上图中连接5和6两个纵线的横向水路)。

    Pear的目标是,修建了这条水路之后,能有尽可能多对水塔和居民点之间能到达。换句话说,设修建之后第i个水塔能到达Ai个点,你要最大化A1+A2+...+An。

    根据定义,这条路必须和X轴平行,但Y坐标不一定要是整数。注意:虽然输入中没有重叠的水路,但是你的方案可以将新修的输水线路与已有的水路重叠。

【输入数据】
    输入第一行包含三个正整数N,M,K,含义如题面所述:N是纵向线数,M横向线数,K是居民点纵坐标。

    接下来M行,每行三个整数。前两个正整数Xi Yi表示水路的起点坐标;
    1<=Xi<=N,0     接下来一个数0或者1,如果是0表示这条水路向左,否则向右。
    保证水路都是合法的,也就是不会流向没有定义的地方。

【输出数据】
输出一行。是一个正整数,即:题目中要求的最大化的A1+A2+...+An。

【输入样例1】
4 3 2
1 1 1
3 1 0
3 1 1
【输出样例1】
11

【输入样例2】
7 9 4
2 3 0
7 2 0
6 3 1
6 1 0
2 1 1
3 3 1
5 2 0
2 2 1
7 1 0
【输出样例2】
21

【数据范围】
对于20%的数据,N,K<=20,M<=100
对于40%的数据,N,K<=100,M<=1000
对于60%的数据,N,K<=1000,M<=100000
对于100%的数据,N,K<=50000,M<=100000


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


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

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

 

 

你可能感兴趣的:(蓝桥杯第五届国赛javab组题目解析)