2013A组题一:
题干
/**
- 2013A组题一:
- 世纪末的星期:
- 曾有邪教称1999年12月31日是世界末日
- 还有人称今后的某个世纪末的12月31日,如果是星期一则会。。。
- 有趣的是,任何一个世纪末的年份的12月31日都不可能是星期一
- 于是,“谣言制造商”有修改为星期日。。。
- 1999年的12月31日是星期五,请问:
- 未来哪一个离我们最近的一个世纪末年(即xx99年)的12月31日正好是星期五(即星期日)?
- 请回答该年份(只写这个4位整数,不要写12月31日等多余信息
*/
public class A_A_2013 {
public static void main(String[] args) {
//创建Calendar对象
Calendar ca = Calendar.getInstance();
for(int year=1999; ;year+=100) {
ca.set(Calendar.YEAR , year);//设置年份为year,以100年速度递增
ca.set(Calendar.MONTH , 11); //12月
ca.set(Calendar.DAY_OF_MONTH , 31);
System.out.println(year+" "+ca.get(Calendar.DAY_OF_WEEK));
if(ca.get(Calendar.DAY_OF_WEEK) == 1) { //1为Sunday
System.out.println(year);
break;
}
}
}
}
2013A组题二:
题干
/**
- 2013A组题二:
- 振兴中华:
- 小明参加了学校的趣味运动会,其中一个项目是跳格子
- 地上画着一些格子,每个格子里写着一个字,如下所示:
- 从我做起振
- 我做起振兴
- 做起振兴中
- 起振兴中华
- 比赛时,先站在左上角的 “从” 字的格子里,可以横向或纵向跳到相邻的格子里
- 但不能跳到对角的格子或其他位置,一直要跳到 “华” 字结束
- 请你帮助小明算一算他一共有多少种可能的跳跃路线?
- 答案是一个整数,请直接提交该数字
*/
public class A_B_2013 {
/**
* 12345
* 23456
* 34567
* 45678
*
* 12345678
*/
/**
* 适合使用递归方法
* 坐标表示:
* (0,0)————(0,4)
* |
* |
* |
* (3,0)————(3,4)
* 只能向右或向下走
* 到达边界即可结束进行计数
* dfs深搜
*/
public static void main(String[] args) {
int ans = f(0 , 0);//起始坐标(0,0)
System.out.println(ans);
}
public static int f(int i,int j) {
if(i==3||j==4) {//到达边界情况
return 1;
}
return f(i+1 , j)+f(i , j+1);//将两种走法的路线数相加
}
}
2013A组题三:
题干
/**
- 2013A组题三:
- 梅森素数:
- 如果一个数字的所有真因子之和等于其本身,则称它为“完全数”或“完美数”
- 例如:6 = 1 + 2 + 3
- 28 = 1 + 2 + 4 +7 +14
- 早在公元前300多年,欧几里得就给出了判定完全数的定理:
- 若2^n-1 是素数,则2^(n-1) * (2^n-1) 是完全数
- 其中 ^ 表示乘方运算,乘方的优先级比四则运算高
- 但人们很快发现,当n很大时,判定一个大数是否为素数到今天也依然是个难题
- 因为发过数学家梅森的猜想,我们习惯上把形如: 2^n-1的素数称为梅森素数
- 截止到2013年2月,一共找到了48个梅森素数,新进找到的素数太大,以至于难于用一半的编程思想
- 窥其全貌,所以我们把任务难度降低:
- 1963年,美国伊利诺伊大学为了纪念他们找到的第23个梅森素数 n = 11213 , 在每个
- 寄出的信封上都印上了 “2^11213-1 是素数” 的字样。
- 2^11213 - 1这个数字已经很大了(有3000多位),请你编程求出这个素数的十进制表示的最后100位
- 该数是一个长度为100的数字串
*/
//考点:BigInteger
public class A_C_2013 {
public static void main(String[] args) {
//BigInteger b 计算2^11213,然后再用封装的方法substract做减法运算
BigInteger b = BigInteger.valueOf (2).pow(11213).subtract(BigInteger.ONE);
String s = b.toString();
int length = s.length();
System.out.println(length);
//截取字符串,填入的是下标
String ans = s.substring(length-100);//从该位开始复制
System.out.println(ans+"\n"+ans.length());
}
}
2013A组题四:
题干
/**
- 2013A组题四:
- 颠倒的价牌:
- 小李的店里专卖其他店中下架的样品电视机,可称为样品电视专卖店
- 其标价都是四位数
- 小李为了标价清晰、方便,使用了预制的类似数码管的标签价
- 只要用颜色笔涂数字就可以了。这个价牌有个特点,有一些数字
- 倒过来看也是合理的数字,如:1 2 5 6 8 9 0 都可以。这样一来,
- 如果牌子倒挂了,有可能完全变成了另一个价格
- 比如:1958 倒挂后就是 8561 差了几千元!
- 当然,多数情况不能倒读,比如:1110,因为0不能作为开始数字。
- 有一点,悲剧终于发生了,某个店员不小心把店里的某两个价格牌给
- 挂倒了,并且两个价格牌的电视机都卖了出去,庆幸的是价格出入不大
- 其中一个赔了两百多,另一个赚了八百多,综合起来,多赚了558元
- 根据这些信息计算:赔钱的那个价牌正确的价格应是多少?
- 答案是一个四位数整数
*/
public class A_D_2013 {
public static void main(String[] args) {
ArrayList<Price> a1 = new ArrayList<Price>();
ArrayList<Price> a2 = new ArrayList<Price>();
//枚举四位数,简单筛选
for(int i=1000;i<10000;i++) {
String s = i+"";
if(s.contains("3")||s.contains("4")||s.contains("7")) continue;
String re_s = reverse(s);//原串翻转
int i1 = 0;//i1为颠倒价格
try {
i1 = Integer.parseInt(re_s.trim());
}catch(NumberFormatException e) {
System.out.println(e);
}
if(i1-i<-200 && i1-i>-300) {
a1.add(new Price(i , i1-i));
}
if(i1-i>800 && i1-i<900) {
a2.add(new Price(i , i1-i));
}
}
//将其颠倒,和原价做差,将赔200多的放入一个集合,赚800多的放入另一个集合
//遍历两个集合两两组合,差值为558就输出
for(Price p1:a1) {//负值
for(Price p2:a2) {
if(p2.plus + p1.plus == 558) {
System.out.println(p1.p+" "+p1.plus);
System.out.println(p2.p+" "+p2.plus);
System.out.println(p1.p);
}
}
}
}
private static String reverse(String s) {
char[] ans = new char[s.length()];
for(int i = s.length()-1 , j = 0 ; i > 0 ; i-- , j++) {
char c = s.charAt(j);
if(c == '6') {
ans[j] = '9';
}else if(c == '9') {
ans[j] = '6';
}else {
ans[j] = c;
}
}
return new String(ans);
}
private static class Price{
int p; //原价
int plus; //差价=颠倒价-原价
public Price(int p ,int plus) {
this.p=p;
this.plus=plus;
}
}
}
2013A组题五:
题干
/**
- 2013A组题五:
- 三部排序:
- 一般的排序有许多的经典算法,如快速排序,希尔排序等等 但实际应用时,经常会或多或少有一些特殊的要求,我们没必要套用那些
- 经典算法,可以根据实际情况建立更好的解法 比如:对一个整数组中的数字进行分类排序: 是的负数都靠左边,整数靠右边,0在中间。
- 注意问题的特点是:负数区域和正数区域并不要求有序,可以利用这个特点通过一次 线性扫描就结束战斗。 以下程序实现了该目标
- static void sort(int[] x) { int p = 0; int left = 0; int right = x.length-1;
- while(p<=right){ if(x[p]<0){ int t =x[left]; x[left] = x[p]; x[p] = t;
- left++; p++; }else if(x[p]>0){ int t = x[right]; x[right] = x[p]; x[p] = t;
- right–; }else{ -----------------; //填写代码 } } }
*/
public class A_E_2013 {
public static void main(String[] args) {
int[] arr = new int[] {25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0};
//测试特殊情况的用例
// int[] arr1 = {};
// int[] arr2 = {0,0,0,0,0,0,0};
// int[] arr3 = {1,1,1,1,1,0};
sort(arr);
System.out.println(Arrays.toString(arr));
}
static void sort(int[] x) {
int p = 0;
int left = 0;
int right = x.length - 1;
//用0为分界点,左边负数,右边正数
while (p <= right) {//p指针在right左边时
if (x[p] < 0) {
//如果p的值小于0,交换
int t = x[left];
x[left] = x[p];
x[p] = t;
left++;//左边指针右移
p++;//p指针右移,此时p指针为负数,左指针的左边也都是负数,左指针和p右移
} else if (x[p] > 0) {//如果p的值大于0,交换
int t = x[right];
x[right] = x[p];
x[p] = t;
right--;//右指针左移,此时右指针位置为正数
} else {//如果p指针的值等于0
// //----------------------; // 填写代码
p++;//正确答案,p指针右移,左指针指向0,即临界值,有负数与0交换
//最终状态:左指针指向0,p指针在右指针右侧
}
}
}
}
2013A组题六:
题干
/**
- 2013年A組題六
- 逆波兰表达式:
- 正常的表达式称为中缀表达式,运算符在中间,主要是给人阅读的
- 机器求解并不方便。例如:3+5*(2+6)-1
- 而且,常常使用逆波兰表达式(前缀表达式表示),上面的算式则表示为:
- 3 * 5 + 2 6 1
- 不在需要括号,机器可以用递归的方法很方便的求解
- 为了简便,我们说:
- 1.只有 + - * 三种运算符
- 2.每个运算数都是一个小于10的非负整数
- 下面的程序对一个逆波兰表达串进行求值
- 其返回值为一个数组,其中一个元素表示求值结果,第二个元素表示
- 它已解析的字符数。
*/
public class A_F_2013 {
//3+5*(2+6)-1
// - + 3 * 5 + 2 6 1
static int[] evaluate(String x) {
if(x.length()==0) return new int[] {0,0};
char c = x.charAt(0); //获得第一个字符
if(c>='0' && c<=9) //这一个字符是0~9
return new int[] {c-'0' , 1};
int[] v1 = evaluate(x.substring(1));//第一个字符是运算符,截取后面的部分
//?可以截取到上一步没有处理到的部分
int[] v2 = evaluate(x.substring( 1+v1[1] )); //填空位置
int v = Integer.MAX_VALUE ;
if(c == '+') v = v1[0] + v2[0];
if(c == '*') v = v1[0] * v2[0];
if(c == '-' ) v = v1[0] - v2[0];
return new int[] {v , 1+v1[1]+v2[1] };
}
}
2013A组题七
题干
/**
- 2013A组题七:
- 错误票据:
- 某涉密单位下发了某种票据,并要在年终全部收回
- 每张票据有唯一的ID号,全年所有票据的ID号是连接的,但ID的开始号码
- 是随机选定的,因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成
- 了某个ID断号,另外一个ID重号。你的任务是通过编程,找出断号的ID和重号的ID
- 假设断号不可能发生在最大和最小号
- 要求程序首先输入一个整数N(N<100)表示后面数据行数
- 接着读入N行数据
- 每行长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000)
- 每个整数代表一个ID号
- 要求输出1行,含两个整数m,n,用空格分隔
- 其中,m表示断号ID ,n表示重号ID
- 例如:
- 用户输入:
- 2
- 5 6 8 11 9
- 10 12 9
- 程序输出:
- 7 9
*/
public class A_G_2013 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
ArrayList<Integer> list = new ArrayList<Integer>();
int N = sc.nextInt();
sc.nextLine(); //吃掉数字后面的换行
for(int i=0 ;i<N;i++) {
String str = sc.nextLine();
String[] res = str.split(" ");
for(int j=0; j<res.length;j++) {
try {
list.add(Integer.parseInt(res[j].trim()));
}catch(NumberFormatException e){
System.out.println(e);
}
}
}
sc.close();
Collections.sort(list);//集合排列
int m=0,n=0;
for(int i=1;i<list.size();i++) {
if(list.get(i)-list.get(i-1)==2)//后一项减去前一项==2,
m = list.get(i)-1;
if(list.get(i)==(list.get(i-1))) //后一项=前一项
n = list.get(i);
}
System.out.println(m+" "+n);
}
2013A组题八
题干
/**
- 2013A组题八,B组题九:
- 带分数:
- 100可以表示为带分数的形式:100 = 3 + 69258 / 714
- 还可以表示为:100 = 82 + 3546 / 197
- 注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)
- 类似这样的带分数,100有11种表示法
- 题目要求:
- 从标准输入读入一个正整数N(N<1000*1000)
- 程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数
- 注意:不要求输出每个表示,只统计有多少表示法
- 例:
- 用户输入:
- 100
- 程序输出:
- 11
- 例:
- 用户输入:
- 105
- 程序输出:
- 6
- 资源约定:
- 峰值内存消耗(含虚拟机)< 64M:
- CPU消耗 < 3000ms
*/
public class A_H_2013 {
/**
* (等号右边)1~9分别一次,分数部分必须能够整除
*/
static int ans = 0;
static int N ;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] arr = {1,2,3,4,5,6,7,8,9};
N = sc.nextInt();
f (arr , 0);
System.out.println(ans);
sc.close();
}
/**
* 确认某一个全排列的第k位
* @param k
*/
private static void f (int[]arr , int k) {
if(k==9) { // 全部确认
check(arr);
// print(arr);
return ;
}
//选定第k位,移交下一层去确认k+1位
for(int i=k;i<arr.length;i++) {
//将第i位与第k位交换
int t = arr[i];
arr[i] = arr[k];
arr[k] = t;
f (arr , k+1);
//回溯(换回来)
t = arr[i];
arr[i] = arr[k];
arr[k] = t;
}
}
/**枚举 + 和 / 的位置**/
private static void check(int[] arr) {
// + 最多放在第七位 X+XXXXX/XXXX --- XXXXXX+XX/X
for(int i=1 ; i<=7 ; i++) {
// / 前面的字符数最多为 9 - i - 1
int num1 = toInt(arr , 0 , i); // + 前面的一段整数
if(num1 >= N) continue; //如果此时+前的数值已经超过了N,则没必要验算了
for(int j=1 ; j<=8-i ; j++) {
int num2 = toInt(arr , i , j);
int num3 = toInt(arr , i+j , 9-i-j);
if(num2%num3==0&&num1+num2/num3==N) {
ans++;
}
}
}
}
private static int toInt(int[] arr, int pos , int len) {
int t = 1;
int ans = 0;
for(int i=pos+len-1;i>=pos;i--) {
ans += arr[i]*t;
t*=10;
}
return ans;
}
}
2013A组题九:
题干
/**
- 2013年A组题九:
- C组题十:
- 剪格子:
- 如图所示,3 X 3 的格子中填写了一些整数
- 我们沿着途中的红色线剪开,得到两个部分,每个部分的数字和都是60
- 本题的要求就是请你编程判定:对于给定的 m X n 的格子中的整数,是否可以
- 分隔成两个部分,使得这两个区域的数字和相等
- 如果存在多种解答,请输入包含左上角格子的那个区域包含的格子的最小数目
- 如果无法分隔,则输出0
- 程序输入输出格式要求:
- 程序先读入两个整数 m n 用空格分隔(m , n < 10)
- 表示表格的宽度和高度
- 接下来是n 行,每行打出m个正整数,用空格分开,每个整数不大于10000
- 程序输出:在所有解中,包含左上角的分割区可能包含的最小的格子数目
- 例如:
- 用户输入:
- 3 3
- 10 1 52
- 20 30 1
- 1 2 3
- 则程序输出:
- 3
- 在例如:
- 用户输入:
- 4 3
- 1 1 1 1
- 1 30 80 2
- 1 1 1 100
- 则程序输出:
- 10
*/
public class A_I_2013 {
/**
* dfs 剪枝 回溯
*/
static int[][] g ;
static int[][] vis; //标记走过的地方
static int m ;
static int n ;
static int total ; //总和
static int ans = Integer.MAX_VALUE ;
static void dfs(int i,int j,int steps,int sum) {
//走出边界或者已走过,非法路径直接return
if(i < 0 || i == n || j<0 || j==m || vis[i][j] ==1) return;
if(sum == total / 2) {
ans = Math.min(ans , steps);//记录步数
return;
}
if(sum > total / 2) return;//总和>总数一半,走不通,回到上一个位置
dfs(i+1 , j , steps+1 , sum+g[i][j] ); //down
dfs(i-1 , j , steps+1 , sum+g[i][j] ); //up
dfs(i , j-1 , steps+1 , sum+g[i][j] ); //left
dfs(i , j+1 , steps+1 , sum+g[i][j] ); //right
vis[i][j] = 0;//回溯,标记为还未经过
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//程序先读入两个整数 m n 用空格分隔(m,n<10)
//表示表格的宽度和高度
m = sc.nextInt();
n = sc.nextInt();
g = new int[n][m];
vis = new int[n][m];
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
g[i][j] = sc.nextInt();
total += g[i][j];
}
}
//完成输入
dfs(0 , 0 , 0 , 0);
System.out.println(ans);
sc.close();
}
}