2014年蓝桥杯国赛Java A组部分题解

1.【结果填空】 标题:海盗分金币

有5个海盗,相约进行一次帆船比赛。
比赛中天气发生突变,他们被冲散了。
恰巧,他们都先后经过途中的一个无名的荒岛,并且每个人都信心满满,觉得自己是第一个经过该岛的人。
第一个人在沙滩上发现了一堆金币。他把金币分成5等份。发现刚好少一个金币。他就从自己口袋拿出一个金币补充进去,然后把属于自己的那份拿走。
第二个到达的人也看到了金币,他也和第一个人一样,把所有金币5等分,发现刚好缺少一个金币,于是自己补进去一个,拿走了属于自己的那份。
第三,第四,第五人的情况一模一样。
等他们到了目的地,都说自己的情况,才恍然大悟,一起去荒岛找金币,然而再也没有找到荒岛。他们都惋惜地说:岛上还有一千多枚金币呢!
请你根据这些信息,推算荒岛上最初有多少金币?
这是一个整数,请通过浏览器提交答案,不要填写任何多余的内容(比如说明性的文字等)

答案:3129
暴破,注意最后一句话是说还剩下1k多金币,也就是一开始至少有1k多金币,暴力的起点也就有了,再用递归进行整除判断,还要满足最后剩下1k多金币。

public class G201401 {
    static boolean solve(int x, int cnt){
        if(x <= 0) return false;
        x++;
        if(x%5 != 0) 
            return false;
        else {
            if(cnt == 4) {
                if((x-x/5) >= 1000 && (x-x/5) < 2000) //剩下
                    return true;
                return false;
            }
            if(solve(x-x/5, cnt+1)) return true;
            else return false;
        }
    }
    public static void main(String[] args) {

        for(int i = 1000; i<=20000; i++){
            if(solve(i, 0)) System.out.println(i);
        }
    }

}

2.【结果填空】 标题:六角幻方

把 1 2 3 ... 19 共19个整数排列成六角形状,如下:
  * * *
 * * * *
* * * * *
 * * * * 
  * * *
要求每个直线上的数字之和必须相等。共有15条直线哦!
再给点线索吧!我们预先填好了2个数字,第一行的头两个数字是:15 13,参见图【p1.png】,黄色一行为所求。
请你填写出中间一行的5个数字。数字间用空格分开。

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

答案:9 6 5 2 16
稍微复杂一点的dfs,特别要注意剪枝,不然时间复杂度O(n!)高的吓人。。以及变量比较多,我用的一维数组存储,打变量名的时候要特别小心。。剪枝有一点小技巧,就是不要一次性的排列好之后再判断,因为这个的复杂度太高,在排列过程中就要进行剪枝判断。。最后,由于1-19的和是确定的,所以可以求出任一行、列或者对角线的和=(1+19)*19/2 / 5 = 38.
#include<bits/stdc++.h>
using namespace std;
int a[16]={1,2,3,4,5,6,7,8,9,11,12,14,16,17,18,19};
bool vis[16];
int b[16];
void print(){
    cout<<15<<" "<<13<<" "<<10<<endl;
    cout<<b[0]<<" "<<b[1]<<" "<<b[2]<<" "<<b[3]<<endl;
    cout<<b[4]<<" "<<b[5]<<" "<<b[6]<<" "<<b[7]<<" "<<b[8]<<endl;
    cout<<b[9]<<" "<<b[10]<<" "<<b[11]<<" "<<b[12]<<endl;
    cout<<b[13]<<" "<<b[14]<<" "<<b[15]<<endl;
    cout<<endl;
}
void dfs(int cur){
    if(cur == 16){
        if(b[1]+b[6]+b[11]+b[15]+15 != 38) return;
        if(b[13]+b[14]+b[15] != 38) return;
        if(b[15]+b[12]+b[8] != 38) return; 
        print();
        return ;
    }
    if(cur == 4){
        if(b[0]+b[1]+b[2]+b[3] != 38) return;
    } 
    if(cur == 5){
        if(b[0]+b[4]+15 != 38) return;
    }
    if(cur == 9){
        if(b[3]+b[8]+10 != 38) return;
        if(b[4]+b[5]+b[6]+b[7]+b[8] != 38) return;
    }
    if(cur == 10){
        if(b[9]+b[5]+b[1]+13 != 38) return;
    }
    if(cur == 13){
        if(13+b[12]+b[2]+b[7] != 38) return;
        if(b[9]+b[10]+b[11]+b[12] != 38) return;
    }
    if(cur == 14){
        if(b[13]+b[10]+b[6]+b[2]+10 != 38) return;
        if(b[9]+b[4]+b[13] != 38) return;
    }
    if(cur == 15){
        if(b[14]+b[11]+b[7]+b[3] != 38) return;
        if(b[0]+b[5]+b[10]+b[14] != 38) return;
    }
    for(int i = 0; i<16; i++){
        if(!vis[i]){
            vis[i] = true;
            b[cur] = a[i];
            dfs(cur+1);
            vis[i] = false;
        }
    }
}
int main(){
    dfs(0);
    return 0;
} 

3.【代码填空】 格子放鸡蛋

X星球的母鸡很聪明。它们把蛋直接下在一个 N * N 的格子中,每个格子只能容纳一枚鸡蛋。它们有个习惯,要求:每行,每列,以及每个斜线上都不能有超过2个鸡蛋。如果要满足这些要求,母鸡最多能下多少蛋呢,有多少种摆放方法呢?
下面的程序解决了这个问题,请仔细分析程序逻辑,推断划线处缺少的代码。

public class A
{
    static int max = 0;
    static int T = 0;
    static final int N = 6;

    // 只能在(r,c) 以及其右,其下放置 
    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<da.length; i++)
        for(int j=0; j<da[i].length; 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<N; i++) if(da[r][i]==1) n++;
        if(n>m) m = n;

        //该列
        n=0;
        for(int i=0; i<N; i++) if(da[i][c]==1) n++;
        if(n>m) m = n;

        //右斜线
        n=0;
        for(int i=0; i<N; i++){ 
            if(r-i<0 || c-i<0) break; 
            if(da[r-i][c-i]==1) n++;
        }
        for(int i=1; i<N; 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<N; i++){ 
            if(r-i<0 || c+i>=N) break; 
            if(da[r-i][c+i]==1) n++;
        }
        for(int i=1; i<N; 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);
    }
}

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

答案:spy(da, r, c) < 2
分析:有点八皇后的味道。。观察到spy()方法没有用上,所以此空一定和spy()方法有关,以及仔细看spy()方法发现返回的是行、列或者斜线上有的鸡蛋的最大个数。以及题目要求不能超过2,结合一下答案就出来了。

4.【编程大题】 标题:排列序数


如果用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,否则按无效代码处理。

乍看这么眼熟,原来之前做的2017蓝桥模拟,天梯赛中都出现了类似的,不过之前是用的暴力的dfs,(流下了只会暴力的泪水.jpg)之前蓝桥模拟那题实际上暴力dfs没跑出来,因为。。。时间复杂度实在是太高。。后来才知道这题算是数论的题,要用到康托展开,(再次流下不学无术的泪水.jpg),其实康托展开(见下式)非常实用,一开始接触可能有点恐惧,慢慢搞清楚就好,(真的好用。

X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!

公式说明:ai为第i位数是在后面所有数(含ai)中的第几小(从0开始编号)的数。举个例子,A={b,a,d,c}
  • 对于b而言,i等于n,b要进行比较的集合是{a,d,c},b在其中排第1位,an=1
  • 对于a而言,i等于n-1,a要进行比较的集合是{d,c},a在其中排第0位,an-1=0
  • 对于d而言,i等于n-2,d要进行比较的集合是{c},d在其中排第1位,an-2=1
  • 最后对于c而言,i等于n-3,c没有要比较的数,an-3=0
所以对于上例,X=1*3!+0*2!+1*1!+0*0!=7,理解之后代码就非常之简单了。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class G201404 {
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static long[] Fac = new long[12];
    static int[] a = new int[12];
    static void getF(int x){
        Fac[1] = 1;
        for(int i = 2; i<=x; i++ ){
            Fac[i] = i*Fac[i-1];
        }
    }
    static void getA(String string){

        for(int i = 0; i<string.length(); i++){
            int sum = 0;
            for(int j = i+1; j<string.length(); j++){
                if(string.charAt(i) > string.charAt(j)) sum++;
            }
            a[i] = sum;
        }

    }
    public static void main(String[] args) throws IOException{
        in.nextToken();
        String string = in.sval; 
        getF(10);
        getA(string);
        long res = 0;
        for(int i = 0; i<string.length(); i++){
            res += Fac[string.length() - i - 1]*a[i];
        }
        out.println(res);
        out.flush();

    }

}

5.【编程大题】 标题:幂一矩阵

天才少年的邻居 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,否则按无效代码处理。

感觉是道数学规律题,但是我只会暴力骗小数据的分,(流下了线代差的泪水.jpg)后面想到更好的方法再更。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class G201405 {
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static int[][] mar,tem;
    static int n,u,v;
    static int[][] quickpow(int x){
        x--;
        int[][] res = mar.clone();
        int[][] m = mar.clone();
        while(x != 0){
            if((x&1) != 0) res = mul(res, m);
            m = mul(m, m);
            x = x >> 1;
        }
        return res;
    }
    static int[][] mul (int[][] a, int[][] b){
        int[][] res = new int[n+1][n+1];

        for(int i = 1; i<=n; i++){
            for(int j = 1; j<=n; j++){
                for(int k = 1; k<=n; k++){
                    res[i][j] += a[i][k]*b[k][j];
                }
            }
        }

        return res;
    }
    static boolean check(){
        for(int i = 1; i<=n; i++){
            for(int j = 1; j<=n; j++){
                if(i == j){
                    if(tem[i][j] != 1) return false;
                }
                else {
                    if(tem[i][j] == 1) return false;
                }
            }
        }
        return true;
    }
    public static void main(String[] args) throws IOException{
        in.nextToken();
        n = (int)in.nval;
        mar = new int[n+1][n+1];
        tem = new int[n+1][n+1];
        for(int i = 0; i<n; i++){
            in.nextToken();
            u = (int)in.nval;
            in.nextToken();
            v = (int)in.nval;
            mar[u][v] = 1;
        }

        for(int i = 0; i<(int)1e9; i++){
            if(i == 0) tem = mar;
            else tem = quickpow(i);
            if(check()){
                out.println(i);
                out.flush();
                break;
            }
        }

    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值