2013Java-B组省赛
题目主要考察对java特殊类class的掌握(Calender、BigDecimal),考察按照给定题目意思,模拟题目的能力,这里的模拟可能需要排序、搜索的帮助。未涉及DP。
一、世纪末的星期(Calender类)
标题: 世纪末的星期
曾有邪教称1999年12月31日是世界末日。当然该谣言已经不攻自破。
还有人称今后的某个世纪末的12月31日,如果是星期一则会....
有趣的是,任何一个世纪末的年份的12月31日都不可能是星期一!!
于是,“谣言制造商”又修改为星期日......
1999年的12月31日是星期五,请问:未来哪一个离我们最近的一个世纪末年(即xx99年)的12月31日正好是星期天(即星期日)?
请回答该年份(只写这个4位整数,不要写12月31等多余信息)
直接用Calender类实现
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
for (int i = 1999; i <= 9999; i = i + 100) {
// 注意日历的声明方式
Calendar calendar = Calendar.getInstance();
// 注意日历中,月份是从0开始
calendar.set(i, 12 - 1, 31);
// 获取这一天是星期几注意星期日从”1“开始
if (calendar.get(Calendar.DAY_OF_WEEK) == 1) {
System.out.println(i);
break;
}
}
}
}
答案:2299
二、马虎的算式(暴力循环)
标题: 马虎的算式
小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。
有一次,老师出的题目是:36 x 495 = ?
他却给抄成了:396 x 45 = ?
但结果却很戏剧性,他的答案竟然是对的!!
因为 36 * 495 = 396 * 45 = 17820
类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54
假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)
能满足形如: ab * cde = adb * ce 这样的算式一共有多少种呢?
请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。
满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。
答案直接通过浏览器提交。
注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。
直接暴力求解即可,五层for循环,每层遍历一位数字。一定要注意题目中说的满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。
public class Main {
public static void main(String[] args) {
int ans = 0;
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
if (j == i) continue;
for (int k = 1; k <= 9; k++) {
if (i == k || j == k) continue;
for (int l = 1; l <= 9; l++) {
if (i == l || j == l || k == l) continue;
for (int m = 1; m <= 9; m++) {
if (i == m || j == m || k == m || l == m) continue;
int a1 = i * 10 + j;
int a2 = k * 100 + l * 10 + m;
int b1 = i * 100 + l * 10 + j;
int b2 = k * 10 + m;
if (a1 * a2 == b1 * b2) {
System.out.println(a1 + "*" + a2 + "=" + b1 + "*" + b2);
ans++;
continue;
}
}
}
}
}
}
System.out.println(ans);
}
}
答案:142
三、振兴中华(DFS搜索)
标题: 振兴中华
小明参加了学校的趣味运动会,其中的一个项目是:跳格子。
地上画着一些格子,每个格子里写一个字,如下所示:(也可参见p1.jpg)
从我做起振
我做起振兴
做起振兴中
起振兴中华
比赛时,先站在左上角的写着“从”字的格子里,可以横向或纵向跳到相邻的格子里,但不能跳到对角的格子或其它位置。一直要跳到“华”字结束。
要求跳过的路线刚好构成“从我做起振兴中华”这句话。
请你帮助小明算一算他一共有多少种可能的跳跃路线呢?
答案是一个整数,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。
把“从我做起振兴中华”,依次标号为1-8,起点位于左上角,每次只能右移或者下移一格,满足题目条件:到达右下角 + (总和=(1+8) * 8 / 2 = 36)。
public class Main {
static int[] x = {0, 1};
static int[] y = {1, 0};
static int[][] vis = new int[8][8];
static int ans = 0;
public static void main(String[] args) {
// 从:1 我:2 做:3 起:4 振:5 兴:6 中:7 华:8
int[][] map = new int[][] {
{1, 2, 3, 4, 5},
{2, 3, 4, 5, 6},
{3, 4, 5, 6, 7},
{4, 5, 6, 7, 8}
};
vis[0][0] = 1;
dfs(map, 0, 0, 1);
System.out.println(ans);
}
static void dfs(int[][] map, int i, int j, int cnt) {
// (1 + 8) * 8 / 2 = 36
if (cnt == 36 && i == 3 && j == 4) {
ans++;
return;
}
for (int k = 0; k < 2; k++) {
int tempx = i + x[k];
int tempy = j + y[k];
if (tempx >= 4 || tempy >= 5 || vis[tempx][tempy] == 1) {
continue;
}
vis[tempx][tempy] = 1;
dfs(map, tempx, tempy, cnt + map[tempx][tempy]);
// 找路线种类,一定要记得回溯
vis[tempx][tempy] = 0;
}
}
}
答案:35
四、黄金连分数(BigDecimal类)
标题: 黄金连分数
黄金分割数0.61803... 是个无理数,这个常数十分重要,在许多工程问题中会出现。有时需要把这个数字求得很精确。
对于某些精密工程,常数的精度很重要。也许你听说过哈勃太空望远镜,它首次升空后就发现了一处人工加工错误,对那样一个庞然大物,其实只是镜面加工时有比头发丝还细许多倍的一处错误而已,却使它成了“近视眼”!!
言归正传,我们如何求得黄金分割数的尽可能精确的值呢?有许多方法。
比较简单的一种是用连分数:
1
黄金数 = ---------------------
1
1 + -----------------
1
1 + -------------
1
1 + ---------
1 + ...
这个连分数计算的“层数”越多,它的值越接近黄金分割数。
请你利用这一特性,求出黄金分割数的足够精确值,要求四舍五入到小数点后100位。
小数点后3位的值为:0.618
小数点后4位的值为:0.6180
小数点后5位的值为:0.61803
小数点后7位的值为:0.6180340
(注意尾部的0,不能忽略)
你的任务是:写出精确到小数点后100位精度的黄金分割值。
注意:尾数的四舍五入! 尾数是0也要保留!
显然答案是一个小数,其小数点后有100位数字,请通过浏览器直接提交该数字。
注意:不要提交解答过程,或其它辅助说明类的内容。
100位小数,对于java来说,可以直接使用BigDecimal类来实现。注意使用大小数divide除法时,一定要指定小数位数,和小数保留方式,否则会报错:Non-terminating decimal expansion; no exact representable decimal result.
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main {
public static void main(String[] args) {
BigDecimal bd = new BigDecimal(1);
BigDecimal tmp = new BigDecimal(1);
for (int i = 0; i < 10000; i++) {
// 指定保留小数位数101位,保留方式四舍五入
BigDecimal ans = bd.divide(bd.add(tmp), 101, RoundingMode.HALF_UP);
tmp = ans;
System.out.println(ans);
System.out.println(ans.scale());
}
}
算到第101位是9,第100位是4,四舍五入后为5。
答案:0.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375
五、六为填空题跳过
七、错误票据(注意输入、部分排序)(排序)
注意我们不知道输入数字的多少,只知道输入的行数,所以可以选择以一行字符串的形式读入数据,再把字符串split形成String[]数组,再把String转成int即可。
注意next是遇到空格就停止,nextLine是遇到回车停止。
把重新处理好的数组进行排序,排序的时候也要注意,我们只需要对第0 - index - 1的数组元素排序,所以要选择部分数组元素进行排序。判断前后两个数组元素是否相等、连续,就可以得到两个答案。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 因为不知道要读入多少数字,所以直接读入字符串next遇到空格停止
// nextLine遇到回车停止
String n = scan.nextLine();
int nn = Integer.parseInt(n);
int[] nums = new int[101];
String[] str = new String[nn];
int index = 0;
for (int i = 0; i < nn; i++) {
str[i] = scan.nextLine();
String[] tmp = str[i].split(" ");
for (int j = 0; j < tmp.length; j++) {
nums[index++] = Integer.parseInt(tmp[j]);
}
}
// 部分排序
// 只对0 - index - 1的位置排序
Arrays.sort(nums, 0, index);
int ans1 = 0;
int ans2 = 0;
for (int i = 1; i < index; i++) {
if (nums[i] == nums[i - 1]) {
ans2 = nums[i];
continue;
}
if (nums[i] != nums[i - 1] + 1) {
ans1 = nums[i - 1] + 1;
continue;
}
}
System.out.printf("%d %d", ans1, ans2);
}
}
八、幸运数(模拟)
按照题目意思模拟,对已经剔除的数用vis数组记录,每次记录剩下的数的下标,能整除就标记为1。最后剩下vis=0的数就是幸运数。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int m = scan.nextInt();
int n = scan.nextInt();
// 标记哪些是幸运数
int[] vis = new int[n + 1];
// tmp用于记录每次的幸运数
int tmp = 2;
boolean flag = true;
while (flag) {
// 记录当前数字的下标
int index = 0;
for (int i = 1; i <= n; i++) {
if (vis[i] == 0) {
index++;
if (index % tmp == 0) {
vis[i] = 1;
}
}
}
// 更新tmp,注意:数字1不考虑
for (int i = tmp + 1; i <= n; i++) {
if (vis[i] == 0) {
tmp = i;
break;
}
if (i == n) {
flag = false;
}
}
}
// 打印结果
int ans = 0;
for (int i = m + 1; i < n; i++) {
if (vis[i] == 0) {
ans++;
}
}
System.out.println(ans);
}
}
九、带分数(全排列、搜索)
先求1-9的全排列,然后对每种组合check,把除法转成乘法,如果满足题意结果++。
求全排列的方法可以参考之前的回溯专题一,需要注意在同一分支中,使用过的数字要用vis标记。
import java.util.*;
public class Main {
static LinkedList<Integer> tmp = new LinkedList<>();
static int ans = 0;
static int[] vis = new int[10];
static int n;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
n = scan.nextInt();
dfs();
System.out.println(ans);
}
static void dfs() {
if (tmp.size() == 9) {
// n = a + b/c
// n*c = a*c + b
// i j k 三个循环依次遍历a b c三个数
int a = 0;
for (int i = 0; i < 9 - 2; i++) {
a = a * 10 + tmp.get(i);
if (a >= n) {
break;
}
int b = 0;
for (int j = i + 1; j < 9 - 1; j++) {
b = b * 10 + tmp.get(j);
int c = 0;
for (int k = j + 1; k < 9; k++) {
c = c * 10 + tmp.get(k);
// 满足题目条件,并且是用完了所有的数
if (n * c == a * c + b && k == 8) {
ans++;
// System.out.println(tmp);
// System.out.println(a + " " + b + " " + c);
break;
}
}
}
}
return;
}
if (tmp.size() > 9) {
return;
}
for (int i = 1; i <= 9; i++) {
if (vis[i] == 1) {
continue;
}
vis[i] = 1;
tmp.add(i);
dfs(i);
// 回溯
tmp.removeLast();
vis[i] = 0;
}
}
}
十、连号区间数(模拟)
首先,1,1 2,2 3,3 4,4…n,n都是,就不用再考虑,直接初始化答案为n。考虑3 2 4 1,对于区间[1,2],找到其中的最大值、最小值,max-min = 2 - 1,最大值和最小值的差值是否等于区间差值,等于的话就找到了新的连号区间。
这道题一开始想法是dp,但是发现状态转移方程推不出来就尝试直接模拟。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
// 1,1 2,2 3,3 ... n,n都是
int ans = n;
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = scan.nextInt();
}
for (int i = 0; i < n; i++) {
int min = nums[i];
int max = nums[i];
// 一直记录最大最小值,不需要再去开第三个循环
for (int j = i + 1; j < n; j++) {
min = Math.min(min, nums[j]);
max = Math.max(max, nums[j]);
if (max - min == j - i) {
ans++;
}
}
}
System.out.println(ans);
}
}