【蓝桥杯】第五届javaA组题解

1.标题:猜年龄

小明带两个妹妹参加元宵灯会。别人问她们多大了,她们调皮地说:“我们俩的年龄之积是年龄之和的6倍”。
小明又补充说:“她们可不是双胞胎,年龄差肯定也不超过8岁啊。”

请你写出:小明的较小的妹妹的年龄。

注意: 只写一个人的年龄数字,请通过浏览器提交答案。不要书写任何多余的内容。

public class Main {
  public static void main(String[] args) {
    for (int i = 1; i < 20; i++) {
      for (int j = 1; j < 20; j++) {
        if (i<j&&i*j==(i+j)*6)
          System.out.println(i+" "+j);
      }
    }
  }
}

2.标题:李白打酒

话说大诗人李白,一生好饮。幸好他从不开车。

一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

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

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

请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。

注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。

class Main
{	
 static long st,et;
 private static int ans;
 //全局变量static

 public static void main(String[] args) {
    f(2,5,10);
    System.out.println(ans);
   }
 static void f(int n,int a,int b)
 {
 	if(n==0)
 	{
 		if(a==0&b==0)
 			ans++;
 		return;
 	}
 	if(a>0)f(2*n,a-1,b);
 	if(b>0)f(n-1,a,b-1);
 }

}

3.标题:神奇算式

由4个不同的数字,组成的一个乘法算式,它们的乘积仍然由这4个数字组成。

比如:

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

都符合要求。

如果满足乘法交换律的算式算作同一种情况,那么,包含上边已列出的3种情况,一共有多少种满足要求的算式。

请填写该数字,通过浏览器提交答案,不要填写多余内容(例如:列出所有算式)。

分析
就是枚举+验证
注意第一个数字0不能开头
验证可以采取,排序+是否相等,遍历+是否都在
为了避免重复,枚举✖的位置在第一个第二个就行,第三个会和第一个重复
四重循环
第一重不能要0,剩余三重0-9,每层开始判断前面选的数字是不是一样,

public class mytest
{
	static int ans;

	public static void main(String[] args)
	{
		for (int i = 1; i < 10; ++i)
		{
			for (int j = 0; j < 10; ++j)
			{
				if (i != j)
					for (int k = 0; k < 10; ++k)
					{
					if (k != i && k != j)
							for (int l = 0; l < 10; ++l)
							{
								if (l != i && l != j && l != k)
								{
									int src = i * 1000 + j * 100 + k * 10 + l;// ijkl四位数
									// 验证
									if (j != 0)
									{
										int r1 = i * (j * 100 + k * 10 + l);// 乘法结果
										if (check(src, r1))
										{
											System.out.printf("%d * %d\n", i, j * 100 + k * 10 + l);
											ans++;
										}
									}
									// 验证
									if (k != 0)
									{
										int r2 = (i * 10 + j) * (k * 10 + l);// 乘法结果
										if ((i * 10 + j) < (k * 10 + l) && check(src, r2))
										{
											System.out.printf("%d *   %d\n", i * 10 + j, k * 10 + l);
											ans++;
										}
									}

								}
							}
					}
			}
		}
		System.out.println(ans);
	}

	private static boolean check(int src, int r2)
	{
		String s1 = String.valueOf(src);
		String s2 = String.valueOf(r2);
		char[] chars1 = s1.toCharArray();
		char[] chars = s2.toCharArray();
		Arrays.sort(chars);
		Arrays.sort(chars1);

		return new String(chars).equals(new String(chars1));
	}
}

子集树+排列树
注意0不能开头


class Main
{
	static long st, et;
	private static int ans;
	// 全局变量static
	static boolean[] mark = new boolean[10];
	static int[] a = new int[4];

	public static void main(String[] args)
	{
		f(0, 0);

		System.out.println(ans/2);
	}

	static void f(int n, int num)
	{
		if (n >=10 || num == 4)
		{
			if (num == 4)
			{
				int j = 0;
				for (int i = 0; i < 10; i++)
				{
					if (mark[i])
					{
						a[j] = i;
						j++;
					}
					if(j==4) break;
				}

				dfs(0);
			}
			return;
		}
		if (num < 4)
		{
			mark[n] = true;
			f(n + 1, num + 1);
			mark[n] = false;
		}
		f(n + 1, num);
	}

	static void dfs(int i)
	{
		if (i >= 4)
		{
			
			if(a[0]!=0)
			{
				for (int j = 0; j < 3; j++)
				{	
					int x = 0, y = 0;
					for (int j2 = 0; j2 <= j; j2++)
					{
						x = x * 10 + a[j2];
					}
					for (int j2 = j + 1; j2 <= 3; j2++)
					{
						y = y * 10 + a[j2];
					}
					String s = String.valueOf(x * y);
					if (s.length() == 4)
					{
						int k;
						for (k = 0; k < 4; k++)
						{
							if (!s.contains(a[k] + ""))
								break;
						}
						if (k == 4) {
							ans++;System.out.println(""+x+"*"+y+"="+x*y);
						}
					}

				}
			}
			return;
		}
		for (int j = i; j < 4; j++)
		{
			int t = a[i];
			a[i] = a[j];
			a[j] = t;
			dfs(i + 1);
			int t1 = a[i];
			a[i] = a[j];
			a[j] = t1;
		}

	}

}

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 = n%3+1;
		System.out.println("write to file: " + filename + " " + msg);
	}
}

5.标题:锦标赛

如果要在n个数据中挑选出第一大和第二大的数据(要求输出数据所在位置和值),使用什么方法比较的次数最少?我们可以从体育锦标赛中受到启发。

如图【1.png】所示,8个选手的锦标赛,先两两捉对比拼,淘汰一半。优胜者再两两比拼…直到决出第一名。

第一名输出后,只要对黄色标示的位置重新比赛即可。

下面的代码实现了这个算法(假设数据中没有相同值)。

代码中需要用一个数组来表示图中的树(注意,这是个满二叉树, 不足需要补齐)。它不是存储数据本身,而是存储了数据的下标。

第一个数据输出后,它所在的位置被标识为-1
分析
代码开启了比较复杂但是是容易猜出来的

public class _05锦标赛 {
  public static void main(String[] args) {
    int a[] = {154,55,18,16,122,17,130,9,58};//原始数据
    A.pick(a);
  }
}
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(a[b[k1]]>a[b[k2]])  //填空
      b[k] = b[k1];
    else
      b[k] = b[k2];
  }
}

6.标题:六角填数

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

使得每条直线上的数字之和都相同。

图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?

请通过浏览器提交答案,不要填写多余的内容。在这里插入图片描述
分析
简单的全排列,自己注意安排好每个空在数组中的位置就好了。

public class  Main {
  static int[] arr = {2, 4, 5, 6, 7, 9, 10, 11, 12};

  public static void main(String[] args) {
    f(0);
  }

  private static void f(int k) {
    if (k == 9) {
      check();
      return;
    }
    for (int i = k; i < 9; i++) {
      int t = arr[k];
      arr[k] = arr[i];
      arr[i] = t;
      f(k + 1);
      t = arr[k];
      arr[k] = arr[i];
      arr[i] = t;
    }
  }

  private static void check() {
    int r1 = 1 + arr[0] + arr[3] + arr[5];
    int r2 = 1 + arr[1] + arr[4] + arr[8];
    int r3 = 8 + arr[0] + arr[1] + arr[2];
    int r4 = 11 + arr[3] + arr[6];
    int r5 = 3 + arr[2] + arr[4] + arr[7];
    int r6 = arr[5] + arr[6] + arr[7] + arr[8];

    if (r1 == r2 && r2 == r3 && r3 == r4 && r4 == r5 && r5 == r6) {
      for (int i = 0; i < 9; ++i) {
        System.out.println(arr[i]+" ");
      }
      System.out.println();
    }
  }
}

7.标题:绳圈

今有 100 根绳子,当然会有 200 个绳头。

如果任意取绳头两两配对,把所有绳头都打结连接起来。最后会形成若干个绳圈(不考虑是否套在一起)。

我们的问题是:请计算最后将形成多少个绳圈的概率最大?

注意:结果是一个整数,请通过浏览器提交该数字。不要填写多余的内容。

分析
这个题目比较难的动态规划 ,而且牵扯到概率,需要有一定的数学推导能力

c[i]:表示用i条绳的2i个端点配对而成的组合种数,而不是绳圈的种数;
c[i]=c[i-1]*(i-1)*2 + c[i-1]*1 =c[i-1]*(2i-1)

  • A.(i-1)个绳子头对儿可以从任意一对儿拆开将新绳子接入而且接入方法有两种(一根绳子两头是不同的),这是2(i-1)种接入方法
  • B.或者自己成一对儿不影响原有配对,这是1种接入方法

设f[i][j]表示i条绳结成j个圈的概率
f[i][1]=f[i-1][1]c[i-1](i-1)*2/c[i]
f[i][i]=1/c[i]——>f[i][i]
f[i][j]=(f[i-1][j]c[i-1](i-1)*2+f[i-1][j-1]*c[i-1])/c[i]

  • A: i跟绳子围城一圈就是等于将i-1跟绳子围成1圈再将第i跟绳子接进去
  • B: i根绳子围成i圈,只有1种方式就是各自围成一圈
  • C:i根绳子围成j圈就是,加入和不加入的两种情况,对应于c[i]的A,B两种情况

最后化简得出递推公式

f[i][j]=(f[i-1][j]*(i-1)*2+f[i-1][j-1])/(2i-1)

确实如果推不出这个递推公式,这个题就很难做了,如果只是组合数还好推,但是加上概率就有点难度了

public class Main{
 public static void main(String[] args) {
   double f[][] = new double[101][101];
   f[1][1] = 1;
   for (int sheng = 2; sheng <= 100; sheng++) {
     f[sheng][1] = f[sheng - 1][1] * (sheng - 1) * 2 / (2 * sheng - 1);
     for (int quan = 2; quan <= sheng; quan++) {
       f[sheng][quan] = (f[sheng - 1][quan] * (sheng - 1) * 2 + f[sheng - 1][quan - 1]) / (2 * sheng - 1);
     }
   }

   double max = -1;
   int ans = -1;
   for (int i = 1; i <= 100; i++) {
     if (f[100][i] > max) {
       max = f[100][i];
       ans = i;
     }
   }
   System.out.println(ans);
 }
}

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
分析
简单的迷宫类型的问题

import java.util.Scanner;

public class Main{
 public static void main(String[] args) {
   Scanner sc = new Scanner(System.in);
   int m = sc.nextInt();
   int n = sc.nextInt();
   int[][] g = new int[m][n];
   for (int i = 0; i < m; i++) {
     for (int j = 0; j < n; j++) {
       g[i][j] = sc.nextInt();
     }
   }
   int x = sc.nextInt();
   int y = sc.nextInt();
   String s = sc.next();
   int d = getD(s);//方向
   int k = sc.nextInt();

   int step=0;
   while (true) {
     //    若蚂蚁在黑格,右转90度,将该格改为白格,并向前移一格;
     if (g[x][y] == 1) {
       d = (d +1)%4;
       g[x][y] = 0;
     } else {//    若蚂蚁在白格,左转90度,将该格改为黑格,并向前移一格。
       d=(d-1+4)%4;
       g[x][y] = 1;
     }
     //前进一步
     if (d == 0) x--;
     if (d == 1) y++;
     if (d == 2) x++;
     if (d == 3) y--;

     step++;
     if (step==k){
       System.out.println(x+" "+y);
       break;
     }
   }
 }

 static int getD(String s) {
   //  UDLR
   if (s.equals("U")) return 0;
   if (s.equals("R")) return 1;
   if (s.equals("D")) return 2;
   if (s.equals("L")) return 3;
   return 0;
 }
}


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,否则按无效代码处理。
在这里插入图片描述
分析
1.首先推出f(1)+…+f(n)的通项公式
2.大整数
3.快速幂logn在10^18才不会超时
4.超级大坑,如果你求斐波拉契第n项的时候使用了mod,但是(a%c)%(b%c)不会抵消c,所以不能这样做!!!
5.用方法重载一个没有mod函数


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

public class Main {
	
	
	static BigInteger MOD;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		 long n = sc.nextLong();
		 long m = sc.nextLong();
		long p = sc.nextLong();
		MOD=BigInteger.valueOf(p);
		f1(n,m,p);
		
	}

	private static void f1(long n, long m, long p) {
		BigInteger sum = fb(n+2).subtract(BigInteger.ONE);
		BigInteger ans =sum;
		if(m<n+2)
		{
			BigInteger fm = fb(m);
			 ans = sum.mod(fm);
		}
		ans = ans.mod(BigInteger.valueOf(p));
		System.out.println(ans);
	}
	

	private static BigInteger fb(long m)
	{
		BigInteger[][] a = {{BigInteger.ONE,BigInteger.ONE}};
		BigInteger[][] b = {{BigInteger.ONE,BigInteger.ONE},{BigInteger.ONE,BigInteger.ZERO}};
		BigInteger[][] c = fz(a, f(b,m-2));
		return c[0][0];
	}

	private static BigInteger[][] f(BigInteger[][]a,long l) {
		BigInteger[][] ans =new BigInteger[a.length][a[1].length];
		for (int i = 0; i < ans.length; i++)
		{
			for (int j = 0; j < ans[0].length; j++)
			{
				if(i==j)
					ans[i][j]=BigInteger.ONE;
				else {
					ans[i][j]=BigInteger.ZERO;
				}
			}
		}
			while (l!=0)
			{
				if((l&1)==1)
				{
					ans=fz(ans,a);
				}
				l=l>>1;
				a=fz(a,a);
			}
			
		
		return ans;
	}

	private static BigInteger[][] fz(BigInteger[][] a1, BigInteger[][] a2)
	{
		BigInteger[][] a3=new BigInteger[a1.length][a2[0].length];
		for (int i = 0; i < a3.length; i++)
		{
			for (int j = 0; j < a3[0].length; j++)
			{
				a3[i][j]=BigInteger.ZERO;
			}
		}
		// TODO Auto-generated method stub
		for (int i = 0; i < a1.length; i++)
		{
			for (int j = 0; j < a1[0].length; j++)
			{
				for (int j2 = 0; j2 < a2[0].length; j2++)
				{
					a3[i][j2]=a3[i][j2].add(a1[i][j].multiply(a2[j][j2]));
				}
			}
		}
		return a3;
	}
	
}


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,否则按无效代码处理。
分析
首先最想到dfs

由s=x+(x+m1)+(x+m1+m2)+…(x+m1+m2+…+mn-1)(每个m取+a,或者-b)
s=nx+mn-1 +2mn-2+…+(n-1)m1=nx+t1a+t2b
(t1+t2=n(n-1)/2)
然后能轻易得出x的上下限
然后再x上下限里经行dfs,外层循环大概O(n),递归肯定比O(n)复杂度高,总的来说时间复杂度大于O(n^2)这对于10^3的数据量是会超时

换种思路

枚举只要满足s-t1a+t2b是n的倍数满足,枚举t1,问题转化成再1…n-1 种选则哪些数,使得和=t1,这就是典型的0-1背包问题,或者说更像一个装在问题,求的是选择的种类数,优化的话可以参考0-1背包的优化


import java.util.Scanner;

public class Main {

  private static int n;
  private static long s;
  private static long a;
  private static long b;

  public static void Main(String[] args) {
    Scanner sc = new Scanner(System.in);
    n = sc.nextInt();
    s = sc.nextLong();
    a = sc.nextLong();
    b = sc.nextLong();

    int t = n * (n - 1) / 2;

    int[] dp = new int[t + 1];
    dp[0] = 1;
    for (int i = 1; i <= n - 1; i++) {
      for (int j = i * (i + 1) / 2; j >= i; j--) {
        // dp[j] += dp[j - i];
        // if (dp[j] > 100000007) dp[j] %= 100000007;
        dp[j] = (dp[j] + dp[j - i]) % 100000007;
      }
    }
    long ans = 0;
    for (int i = 0; i <= t; i++) {
      if ((s - i * a + (t - i) * b) % n == 0)
        ans = (ans + dp[i]) % 100000007;
   
    }
    System.out.println(ans);
  }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值