第一题:外星日历:
某星系深处发现了文明遗迹。
他们的计数也是用十进制。
他们的文明也有日历。日历只有天数,没有年、月的概念。
有趣的是,他们也使用了类似“星期”的概念,
只不过他们的一个星期包含了9天,
为了方便,这里分别记为: A,B,C....H,I*
从一些资料上看到,
他们的23日是星期E
他们的190日是星期A
他们的343251日是星期I
令人兴奋的是,他们居然也预见了“世界末日”的那天,
当然是一个很大很大的数字
651764141421415346185
请你计算一下,这遥远的一天是该文明的星期几?
你需要提交的是一个大写字母,表示该文明的星期几
代码实现:
package 蓝桥2017;
import java.math.BigInteger;
//System.out.println(23%9);//5——》E
//System.out.println(190%9);//1———》A
//System.out.println(343251%9);//0——》I
//根据以上测试:用 天数%9 得到的余数 余数对应星期:
// A到H 8天对应数字1到8 而I对应0
// System.out.println(651764141421415346185%9);//说此数超过了int的范围
//long num = 651764141421415346185L ;此处又说超过long的范围,那该如何??
/*分析:
* 1. 在java中遇到比long 类型还大的数,要用BigInteger进行保存
* BigInteger num = new BigInteger(""); 参数要求是String
* 2. 但此处没有 num%9 这种运算,要使用它的函数mod(BigInteger num2)
* 参数仍然是BigInteger类型的; 返回的结果是BigInteger类型*/
public class T1 {
public static void main(String[] args) {
BigInteger num = new BigInteger("651764141421415346185");
BigInteger num2 = new BigInteger("9");
BigInteger num3 = num.mod(num2);
System.out.println(num3);
//7 则对应G
}
}
第二题:兴趣小组
为丰富同学们的业余文化生活,某高校学生会创办了3个兴趣小组
(以下称A组,B组,C组)。
每个小组的学生名单分别在【A.txt】,【B.txt】和【C.txt】中。
每个文件中存储的是学生的学号。
由于工作需要,我们现在想知道:
既参加了A组,又参加了B组,但是没有参加C组的同学一共有多少人?
请你统计该数字并通过浏览器提交答案。
注意:答案是一个整数,不要提交任何多余的内容。
笨笨有话说:
哇塞!数字好多啊!一眼望过去就能发现相同的,好像没什么指望。
不过,可以排序啊,要是每个文件都是有序的,那就好多了。
歪歪有话说:
排什么序啊,这么几行数字对计算机不是太轻松了吗?
我看着需求怎么和中学学过的集合很像啊.....
说明:题目在学生名单中的各个学号以逗号隔开的,每个学号8位
我自己在实现此功能,文件内容是自己打的
代码实现:
package 蓝桥2017;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/*fenxi:
* 因为学生名单中的各个学号以逗号隔开的,其数据格式正好和数组的格式相同
* 思路1:就是将三个文件内容存放在三个数组中,然后遍历三个数组
* 思路2:用java语言去读取文件,用分割将各个学号分开之后,也是存到数组中*/
public class T2 {
// public static void main(String[] args) {
// //有的会以0开头,可以统一去掉0 题目的目的就是找相同的学号就行
// int[] a = {20200123,20200124,20200125};
// int[] b = {20200123,20200124,20200125,20200126};
// int[] c = {20200122,20200133,20200144};
// //因为条件判断要:判断某个元素在某集合中; 所以将三个数组的都放到三个集合中去,再调用contains方法
// List<Integer> lista = new ArrayList<>();
// List<Integer> listb = new ArrayList<>();
// List<Integer> listc = new ArrayList<>();
// for(int i=0;i<a.length;i++) {
// lista.add(a[i]);
// }
// for(int i=0;i<b.length;i++) {
// listb.add(b[i]);
// }
// for(int i=0;i<c.length;i++) {
// listc.add(c[i]);
// }
// //遍历集合a,取出元素,条件:包含在集合b中,不包含在集合c中
// int count = 0;
// for(int i=0;i<lista.size();i++) {
// int num = lista.get(i);
// if(listb.contains(num)&&!listc.contains(num)) {
// count++;
// }
// }
// System.out.println(count);//3
// }
// 因为用到多次读文件的操作,所以将其封装为一个方法 参数是文件的路径 返回值就是此路径下的文件的内容。
public static String readFile(String path) throws IOException {
FileInputStream f = new FileInputStream(path);
// 定义字节数组来存储读取的内容 数组的长度可以指为整个文件的大小 这样调用int read(byte[] b) 一次就可以将文件数据读取完 读取到此字节数组中
int size = f.available();
byte[] arrText = new byte[size];
f.read(arrText);// 此时数组中存放了文件所有内容
// 将字节数组转为字符串
String string = new String(arrText);
f.close();
return string;
}
public static void main(String[] args) throws IOException {
String arrA = readFile("E:\\aa.txt");
String arrB = readFile("E:\\bb.txt");
String arrC = readFile("E:\\cc.txt");
// 对空格和回车处理:
arrA.replace(" ", "").replace("\r\n", "");
arrB.replace(" ", "").replace("\r\n", "");
arrC.replace(" ", "").replace("\r\n", "");
// 对文件切割
String[] a = arrA.split(",");
String[] b = arrB.split(",");
String[] c = arrC.split(",");
// 将三个数组的都放到三个集合中去
List<String> lista = new ArrayList<>();
List<String> listb = new ArrayList<>();
List<String> listc = new ArrayList<>();
for (int i = 0; i < a.length; i++) {
lista.add(a[i]);
}
for (int i = 0; i < b.length; i++) {
listb.add(b[i]);
}
for (int i = 0; i < c.length; i++) {
listc.add(c[i]);
}
// 遍历集合a,取出元素,条件:包含在集合b中,不包含在集合c中
int count = 0;
for (int i = 0; i < lista.size(); i++) {
String num = lista.get(i);
if (listb.contains(num) && !listc.contains(num)) {
count++;
}
}
System.out.println(count);// 3
}
}
/*
1. int available() :返回流当中剩余的没有读到的字节数量
2. FileInputStream读取字节数据:
* 1.构造方法:FileInputStream(String 路径名) 2. 使用字节数组读取:
* int read(byte[]b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
* 说明:最重要的是每次读取数据都是从头开始放,第二次读取的数据可能覆盖第一次数据,若没有覆盖完,第一次数据也会和第二次读的数据一起输出
*/
第三题:纸牌三角形
A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算)。要求每个边的和相等。
下图就是一种排法 :
A
9 6
4 8
3 7 5 2
这样的排法可能会有很多。
如果考虑旋转、镜像后相同的算同一种,一共有多少种不同的排法呢?
请你计算并提交该数字。
注意:需要提交的是一个整数,不要提交任何多余内容。
笨笨有话说:
感觉可以暴力破解哦。
麻烦的是,对每个排法还要算出它的旋转、镜像排法,看看有没有和历史重复。
歪歪有话说:
人家又不让你把所有情况都打印出来,只是要算种类数。
对于每个基本局面,通过旋转、镜像能造出来的新局面数目不是固定的吗?
代码实现:
package 蓝桥2017;
/*fenxi:
* 是否用全排列?再对每一种情况实现“每个边的和相等”这个算法??? 那旋转、镜像如何排除??
* 对于图中的A顺时针旋转:可以依次旋转到2和3的位置 还有镜像 那一共是6种
* 先将旋转和镜像当作不同的,最后需要除以6 */
public class T3 {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
sort(arr, 0,arr.length-1);
System.out.println(count/6);
}//144
static int count = 0;
public static void sort(int[] arr, int start, int end) {
if (start == end) {
int bian1 = arr[0] + arr[1] + arr[3] + arr[5];
int bian2 = arr[0] + arr[2] + arr[4] + arr[8];
int bian3 = arr[5] + arr[6] + arr[7] + arr[8];
if (bian1 == bian2 && bian1 == bian3) {
count++;
}
return;
}
for (int i = start; i <= end; i++) {
swap(arr, start, i);
sort(arr, start + 1, end);
swap(arr, start, i);
}
}
public static void swap(int[] arr, int i, int j) {
int c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
}
第四题:承压计算:
X星球的高科技实验室中整齐地堆放着某批珍贵金属原料。
每块金属原料的外形、尺寸完全一致,但重量不同。
金属材料被严格地堆放成金字塔形。
7
5 8
7 8 8
9 2 7 2
8 1 4 9 1
8 1 8 8 4 1
7 9 6 1 4 5 4
5 6 5 5 6 9 5 6
5 5 4 7 9 3 5 5 1
7 5 7 9 7 4 7 3 3 1
4 6 4 5 5 8 8 3 2 4 3
1 1 3 3 1 6 6 5 5 4 4 2
9 9 9 2 1 9 1 9 2 9 5 7 9
4 3 3 7 7 9 3 6 1 3 8 8 3 7
3 6 8 1 5 3 9 5 8 3 8 1 8 3 3
8 3 2 3 3 5 5 8 5 4 2 8 6 7 6 9
8 1 8 1 8 4 6 2 2 1 7 9 4 2 3 3 4
2 8 4 2 2 9 9 2 8 3 4 9 6 3 9 4 6 9
7 9 7 4 9 7 6 6 2 8 9 4 1 8 1 7 2 1 6
9 2 8 6 4 2 7 9 5 4 1 2 5 1 7 3 9 8 3 3
5 2 1 6 7 9 3 2 8 9 5 5 6 6 6 2 1 8 7 9 9
6 7 1 8 8 7 5 3 6 5 4 7 3 4 6 7 8 1 3 2 7 4
2 2 6 3 5 3 4 9 2 4 5 7 6 6 3 2 7 2 4 8 5 5 4
7 4 4 5 8 3 3 8 1 8 6 3 2 1 6 2 6 4 6 3 8 2 9 6
1 2 4 1 3 3 5 3 4 9 6 3 8 6 5 9 1 5 3 2 6 8 8 5 3
2 2 7 9 3 3 2 8 6 9 8 4 4 9 5 8 2 6 3 4 8 4 9 3 8 8
7 7 7 9 7 5 2 7 9 2 5 1 9 2 6 5 3 9 3 5 7 3 5 4 2 8 9
7 7 6 6 8 7 5 5 8 2 4 7 7 4 7 2 6 9 2 1 8 2 9 8 5 7 3 6
5 9 4 5 5 7 5 5 6 3 5 3 9 5 8 9 5 4 1 2 6 1 4 3 5 3 2 4 1
X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X
其中的数字代表金属块的重量(计量单位较大)。
最下一层的X代表30台极高精度的电子秤。
假设每块原料的重量都十分精确地平均落在下方的两个金属块上,
最后,所有的金属块的重量都严格精确地平分落在最底层的电子秤上。
电子秤的计量单位很小,所以显示的数字很大。
工作人员发现,其中读数最小的电子秤的示数为:2086458231
请你推算出:读数最大的电子秤的示数为多少?
注意:需要提交的是一个整数,不要填写任何多余的内容。
笨笨有话说:
不断的除2,加到下面,除2,加到下面,.... 不会浮点精度溢出吧?
歪歪有话说:
怕除不开还不好办, 把每个数字扩大一定的倍数不就好了。
代码实现:
package 蓝桥2017;
import java.util.Arrays;
import java.util.Scanner;
/*分析:每块原料的重量都十分精确地平均落在下方的两个金属块上
* 是指:第一层的7 而7/2=3.5 那第二层的5+3.5 8+3.5
* 重量依次往下传递:最后,所有的金属块的重量都严格精确地平分落在最底层的电子秤上。
* 在不停地除2,加到下面,可能会出现 浮点精度溢出;根据提示,把每个数字扩大一定的倍数再去除
* 思路:
* 1.用二维数组来存放所有的金属块的重量,就是每层都靠左对齐
* 2.每个数字扩大一定的倍数再去除2 进行累加
* 3.通过第一层,计算出第二层,依次往下,最后计算出每个X所承载的重量
* 4.已知读数最小的电子秤的示数,按照一定的比例,就可以计算出最大的电子示数(因为之前每个数字都扩大了)
* */
public class T4 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double[][] arr = new double[30][30];
// 依次往数组中存数据
for (int i = 0; i < arr.length - 1; i++) {// 最后一行是电子秤不需要输入
for (int j = 0; j <= i; j++) {// 每一行输入i个数
arr[i][j] = scanner.nextDouble();
}
}
// 把每个数字扩大一定的倍数
for (int i = 0; i < arr.length - 1; i++) {// 最后一行是电子秤不需要输入
for (int j = 0; j <= i; j++) {// 每一行输入i个数
arr[i][j] = arr[i][j] * Math.pow(2, 30);//2的30次方
}
}
// 从第二层开始,
//非第一列: 每块的重量公式:arr[i][j] = arr[i][j]+arr[i-1][j-1]/2+arr[i-1][j]/2
//第一列: 每块的重量公式:arr[i][j] = arr[i][j]+arr[i-1][j]/2
for (int i = 1; i < arr.length; i++) {// 从第二行开始,对数组的所有行
for (int j = 0; j <= i; j++) {
if (j == 0) {
arr[i][j] = arr[i][j]+arr[i - 1][j] / 2;
} else {
arr[i][j] = arr[i][j] + arr[i - 1][j - 1] / 2 + arr[i - 1][j] / 2;
}
}
}
// 对二维数组的最后一行进行排序,求最大值和最小值
Arrays.sort(arr[29]);
// 是升序排列
double min = arr[29][0];
double max = arr[29][29];
// System.out.println(min+" "+max); //4.172916462E9 1.45330385328E11
// min/电子秤最小值 == max/电子秤最大值
//而我们要求的是: 电子秤最大值??
double result = max*2086458231/min;
System.out.println(result);
//7.2665192664E10 答案是72665192664
}
}
//最大值:Double.MAX_VALUE=1.7976931348623157E308 (2的1024次方-1)
//最大值:Integer.MAX_VALUE= 2147483647 (2的31次方-1)
//最大值:Long.MAX_VALUE=9223372036854775807 (2的63次方-1)
第五题:杨辉三角
题目描述
杨辉三角也叫帕斯卡三角,在很多数量关系中可以看到,十分重要。
第0行: 1
第1行: 1 1
第2行: 1 2 1
第3行: 1 3 3 1
第4行: 1 4 6 4 1
....
两边的元素都是1, 中间的元素是左上角的元素与右上角的元素和。我们约定,行号,列号都从0计数。
所以: 第6行的第2个元素是15,第3个元素是20
直观地看,需要开辟一个二维数组,其实一维数组也可以胜任。
如下程序就是用一维数组“腾挪”的解法
public class A
{
// 杨辉三角形的第row行第col列
static long f(int row, int col){
if(row<2) return 1;
if(col==0) return 1;
if(col==row) return 1;
long[] a = new long[row+1];
a[0]=1;
a[1]=1;
int p = 2;
while(p<=row){
a[p] = 1;
for( __________________ ) a[q] = a[q] + a[q-1];
p++;
}
return a[col];
}
public static void main(String[] args){
System.out.println(f(6,2));
System.out.println(f(6,3));
}
}
请仔细分析源码,并完成划线部分缺少的代码。
注意:只提交缺少的代码,不要提交已有的代码和符号。也不要提交说明性文字。
代码实现:
package 蓝桥2017;
/*
第0行: 1
第1行: 1 1
第2行: 1 2 1
第3行: 1 3 3 1
第4行: 1 4 6 4 1
f函数实现给定行号和列号可以得到对应的数值*/
public class T5 {
// 杨辉三角形的第row行第col列
static long f(int row, int col) {
if (row < 2)// 第一行和第二行
return 1;
if (col == 0)// 第一列
return 1;
if (col == row)// 最后一列
return 1;
long[] a = new long[row + 1];
a[0] = 1;
a[1] = 1;
int p = 2;// 是变化的行号
while (p <= row) {
a[p] = 1;// 每行的最后一列是1
for (int q = p - 1; q >= 1; q--)
a[q] = a[q] + a[q - 1];
p++;
}
return a[col];
}
public static void main(String[] args) {
System.out.println(f(3, 1));
System.out.println(f(6, 2));
System.out.println(f(6, 3));
}
}
第六题:最大公共子串
题目描述
最大公共子串就是求两个串的所有子串中能够匹配上的最大长度是多少。
比如:"abcdkkk" 和 "baabcdadabc",
可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。
下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。
请分析该解法的思路,并补全划线部分缺失的代码
public class A
{
static int f(String s1, String s2)
{
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
int[][] a = new int[c1.length+1][c2.length+1];
int max = 0;
for(int i=1; i<a.length; i++){
for(int j=1; j<a[i].length; j++){
if(c1[i-1]==c2[j-1]) {
a[i][j] = _______________________ ; //填空
if(a[i][j] > max) max = a[i][j];
}
}
}
return max;
}
public static void main(String[] args){
int n = f("abcdkkk", "baabcdadabc");
System.out.println(n);
}
}
注意:只提交缺少的代码,不要提交已有的代码和符号。也不要提交说明性文字。
package 蓝桥2017;
public class T6 {
static int f(String s1, String s2){
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
int[][] a = new int[c1.length+1][c2.length+1];
int max = 0;
for(int i=1; i<a.length; i++){
for(int j=1; j<a[i].length; j++){
if(c1[i-1]==c2[j-1]) {
a[i][j] = a[i-1][j-1]+1 ; //填空
if(a[i][j] > max) max = a[i][j];
}
}
}
/*分析: a[i][j] 就是统计当前公共字串字符的个数
* 可以列一个二维矩阵,将a[i][j]填充进去,发现:记录两个串中的字符连续第几次相等 == a[i-1][j-1]+1 */
return max;
}
public static void main(String[] args) {
int n = f("abcdkkk", "baabcdadabc");
System.out.println(n);
}
}
第七题: Excel地址
题目描述 :
Excel单元格的地址表示很有趣,它使用字母来表示列号。
比如,
A表示第1列,
B表示第2列,
Z表示第26列,
AA表示第27列,
AB表示第28列,
BA表示第53列,
....
当然Excel的最大列号是有限度的,所以转换起来不难。
如果我们想把这种表示法一般化,可以把很大的数字转换为很长的字母序列呢?
本题目既是要求对输入的数字, 输出其对应的Excel地址表示方式。
例如,
输入:
26
则程序应该输出:
Z
再例如,
输入:
2054
则程序应该输出:
BZZ
我们约定,输入的整数范围[1,2147483647]
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
笨笨有话说:
这有点像进制关系,又不完全是。好像末2位是以1当26,末3位是以1当26*26
歪歪有话说:
要是从字母序列转数字还好点,倒过来有点麻烦,不过计算机跑得快啊。
思路1(采用歪歪的方法)
package 蓝桥2017;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/*fenxi:
对于字母A到Z的组合多位字符串,A到Z代表数字1到26的数字,类比于数字的个位,十位,百位等,
个位就是单个数字,就是个位上的数字 十位就是26*十位数的数字 百位就是26*26*百位数的数字
比如:BA== 26*2+1=53 BZZ == 2*26*26+26*26+26
疑问:如何确定输入的数字是几位数?? 怎么表示个,十,百,千位等?到底要最大多少位??
思路:
思路1:定义一个集合,起初只有一个数值a=1,使a++,a从1到26变化,
然后再添加一个元素b=1,把原来的a=26变为1,再使a++直到a=26
把b=2,a=1 再使a++直到a=26
依次往下,直到b=26
最后集合中会有一些元素数字,将数字一一与字母进行对应
*/
public class T7 {
public static void main(String[] args) {
// 定义一个集合
List<Integer> list = new ArrayList<>();
list.add(0);
Scanner scanner = new Scanner(System.in);
long n = scanner.nextLong();
for (int i = 1; i <= n; i++) {
// 集合中最后一个元素加1
int num = list.get(list.size() - 1) + 1;
// 需要讨论,当集合中最后一个元素是26时的特殊情况,类似于模拟“进位”现象
if (num <= 26) {
// 对于集合中最后一个元素不是26时,直接更新最后一个元素值
list.set(list.size() - 1, num);
} else {// 当集合中最后一个元素是26时,要进位,可能会出现1999这样的情况,并不是进一位,所以分情况
// 当集合中只有一个数字26,就直接添加一位数字1,并将26赋值为1,此时集合为{1,1}
if (list.size() == 1) {
list.add(0, 1);// 就直接添加一位数字1
list.set(1, 1);// 并将26赋值为1
}
// 当集合不只有一个数字26,{2,2,26}等,把26前面的元素需要进行一个循环,判断是否会出现像十进制中的1999的情况,
// 从倒数第二位开始判断是在那位上加一 比如{1,26,26} 在倒数第三位上加1 变为{2,1,1}
else {
boolean isJiaYi = false;// 假设一直没有加1操作
for (int j = list.size() - 2; j >= 0; j--) {
if (list.get(j) < 26) {
list.set(j, list.get(j) + 1);
for (int j2 = j + 1; j2 < list.size(); j2++) {
list.set(j2, 1);
}
isJiaYi = true;
break;
}
}
if (isJiaYi == false) {// 还有极端情况:{26,26,26}——》{1,1,1,1}
list.add(0, 1);
for (int j2 = 1; j2 < list.size(); j2++) {
list.set(j2, 1);
}
}
}
}
}
// A的ASCII码值是65
// 集合中的数字是1到26 我们将取出的数字变为1+64=65等等 再将65转换为A
String result = "";
for (int i = 0; i < list.size(); i++) {
result = result + (char) (list.get(i) + 64);
}
System.out.println(result);
}
}
思路2(采用笨笨的方法)(更加推荐)
package 蓝桥2017;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/*比较像 26进制
* 整数十进制X转换为n进制:X/n=a1....b1 若a1不为0,a1/n=a2...b2 若此时a2=0,即商为0,则逆序组合余数对应:b2b1
* 28/26=1...2 1/26=0...1 即对应12——》AB
* 但对于:52/26=2...0 2/26=0...2 20-->A? 因为0没有对应的字母
* 解决:(1)遇到余数0,就将其变为26 ,然后商结果要有减一操作
* 52/26=2...26 1/26=0...1 1 26-->AZ
* 我们将各个余数存起来,最后再对应字母 */
public class T7_02 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
long n = input.nextLong();
List<Integer> list = new ArrayList<>();
// 循环中不停除以26 次数未知
while (true) {
int mod = (int) (n % 26);// 余数
if (mod == 0) {// 可以除尽,遇到余数0,就将其变为26 ,然后商结果要有减一操作
list.add(26);
n--;
}
else {
list.add(mod);
}
n = n / 26;
if (n == 0) {
break;
}
}
// A的ASCII码值是65
// 集合中的数字是1到26 我们将取出的数字变为1+64=65等等 再将65转换为A
String result = "";
for (int i = list.size() - 1; i >= 0; i--) {// 逆序组合余数
result = result + (char) (list.get(i) + 64);
}
System.out.println(result);
}
}
第八题:拉马车
题目描述
小的时候,你玩过纸牌游戏吗?
有一种叫做“拉马车”的游戏,规则很简单,却很吸引小朋友。
其规则简述如下:
假设参加游戏的小朋友是A和B,游戏开始的时候,他们得到的随机的纸牌序列如下:
A方:[K, 8, X, K, A, 2, A, 9, 5, A]
B方:[2, 7, K, 5, J, 5, Q, 6, K, 4]
其中的X表示“10”,我们忽略了纸牌的花色。
从A方开始,A、B双方轮流出牌。
当轮到某一方出牌时,他从自己的纸牌队列的头部拿走一张,放到桌上,并且压在最上面一张纸牌上(如果有的话)。
此例中,游戏过程:
A出K,B出2,A出8,B出7,A出X,此时桌上的序列为:
K,2,8,7,X
当轮到B出牌时,他的牌K与桌上的纸牌序列中的K相同,则把包括K在内的以及两个K之间的纸牌都赢回来,放入自己牌的队尾。注意:为了操作方便,放入牌的顺序是与桌上的顺序相反的。
此时,A、B双方的手里牌为:
A方:[K, A, 2, A, 9, 5, A]
B方:[5, J, 5, Q, 6, K, 4, K, X, 7, 8, 2, K]
赢牌的一方继续出牌。也就是B接着出5,A出K,B出J,A出A,B出5,又赢牌了。
5,K,J,A,5
此时双方手里牌:
A方:[2, A, 9, 5, A]
B方:[Q, 6, K, 4, K, X, 7, 8, 2, K, 5, A, J, K, 5]
注意:更多的时候赢牌的一方并不能把桌上的牌都赢走,而是拿走相同牌点及其中间的部分。但无论如何,都是赢牌的一方继续出牌,有的时候刚一出牌又赢了,也是允许的。
当某一方出掉手里最后一张牌,但无法从桌面上赢取牌时,游戏立即结束。
对于本例的初始手牌情况下,最后A会输掉,而B最后的手里牌为:
9K2A62KAX58K57KJ5
本题的任务就是已知双方初始牌序,计算游戏结束时,赢的一方手里的牌序。当游戏无法结束时,输出-1。
输入为2行,2个串,分别表示A、B双方初始手里的牌序列。
输出为1行,1个串,表示A先出牌,最后赢的一方手里的牌序。
例如,
输入:
96J5A898QA
6278A7Q973
则程序应该输出:
2J9A7QA6Q6889977
再比如,
输入:
25663K6X7448
J88A5KJXX45A
则程序应该输出:
6KAJ458KXAX885XJ645
我们约定,输入的串的长度不超过30
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
笨笨有话说:
不断删除前边的,又要后边添加.... 如果用数组,需要开一个大点的,请佛祖保佑在游戏结束前,不会用到数组的边缘。
歪歪有话说:
反正串也不长,不如每次操作都返回一个新的串。
默默有话说:
我一般都不吱声,这是典型的队列结构,动态数组最好,没有?自己造一个呗!
代码实现:
package 蓝桥2017;
import java.security.PublicKey;
import java.util.Scanner;
/*简介:
* 1.双方轮流出牌,当轮到某一方出牌时,他从自己的纸牌队列的头部拿走一张
* 2.赢牌的一方并不能把桌上的牌都赢走,而是拿走相同牌点及其中间的部分
* 3.赢牌的一方继续出牌
* 4.当某一方出掉手里最后一张牌,但无法从桌面上赢取牌时,游戏立即结束。
* 5.当游戏无法结束时,输出-1。
* 思路:
* 1.使用StringBuilder类代表可变字符串对象
* StringBuilder a = new StringBuilder(String str) 构造一个初始化为指定字符串内容的字符串构建器。
* append 方法始终将这些字符添加到生成器的末端;而 insert 方法则在指定的点添加字符
*/
public class T8 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
StringBuilder strA = new StringBuilder(input.next());
StringBuilder strB = new StringBuilder(input.next());
StringBuilder strDesk = new StringBuilder("");// 桌上的牌
char AB = 'A';// 代表A出牌(初始状态)
// 模拟出牌,总次数未知
while (true) {
// 若是A出牌,需要改变strA和strDesk
if (AB == 'A') {
Boolean isWin = playPoker(strA, strDesk);// 是否赢牌
if (strA.length() == 0) {// A手中没有牌了,A输了
System.out.println(strB.toString());
break;
}
// 若游戏没有结束,就判断A是否赢牌了,赢了,下次还是A先出牌
AB = isWin == true ? 'A' : 'B';
}
// 若是B出牌,需要改变strB和strDesk
else {
if (AB == 'B') {
Boolean isWin = playPoker(strB, strDesk);// 是否赢牌
if (strB.length() == 0) {// B手中没有牌了,B输了
System.out.println(strA.toString());
break;
}
// 若游戏没有结束,就判断B是否赢牌了,赢了,下次还是B先出牌
AB = isWin == true ? 'B' : 'A';
}
}
}
}
// 封装一个函数,实现A/B出牌时改变各自手中的牌,还有桌子上的牌,返回true/false,代表有没有赢牌 这样可以判断下一次谁先出牌
public static boolean playPoker(StringBuilder strPlayer, StringBuilder strDesk) {
// 出牌时,手上的牌删掉一张,桌上多一张牌
char ch = strPlayer.charAt(0);// 记录要出的牌
strPlayer.deleteCharAt(0);
strDesk.append(ch);
// 当前出的这张牌,是否桌上已经有了,就是赢牌
if (strDesk.lastIndexOf(ch + "", strDesk.length() - 2) != -1) { // string1.lastIndexOf(strin2,startIndex)
// 是从startIndex右往左着找,string1中是否包含字串string2,找到返回下标,否则返回-1
// 赢牌,拿走相同牌点及其中间的部分,手上的牌要增加(注意放到手中的顺序与在桌子上的顺序是相反的),桌上的牌要减少
int index = strDesk.indexOf(ch + "");// 桌上与出的牌相同的牌所在的下标
for (int i = strDesk.length() - 1; i >= index; i--) {
strPlayer.append(strDesk.charAt(i));
}
strDesk.delete(index, strDesk.length()); // StringBuilder.delete(startIndex,endIndex)
// 删除时,包含startIndex的元素到endIndex,但不包含endIndex处的元素,即前闭后开
return true;
} else {
return false;
}
}
}
/*
* 当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。 和 String 类不同的是,StringBuffer
* 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。 StringBuilder 类在 Java 5 中被提出,它和
* StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的,由于 StringBuilder 相较于
* StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
*/
第九题:青蛙跳杯子
题目描述:
X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。
*WWWBBB
其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。
X星的青蛙很有些癖好,它们只做3个动作之一:
1. 跳到相邻的空杯子里。
2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。
对于上图的局面,只要1步,就可跳成下图局面:
WWW*BBB
本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。
输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。
例如:
输入:
*WWBB
WWBB*
则程序应该输出:
2
再例如,
输入:
WWW*BBB
BBB*WWW
则程序应该输出:
10
我们约定,输入的串的长度不超过15
笨笨有话说:
我梦见自己是一棵大树,
青蛙跳跃,
我就发出新的枝条,
春风拂动那第 5 层的新枝,
哦,我已是枝繁叶茂。
代码实现:
package 蓝桥2017;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
/*思路:
* 1. 将初始串s1 和 结果串s2进行比较,若是相等的,结果0步
* 2. s1!=s2 就对s1按照题目的一步可以演变出它的3个下一种状态:接着把这3中状态分别与s2进行比较,若有一个相等,则程序结束,只需要一步
* 3. 一步的3种状态没有与s2相等的:再去对一步的3种状态分别再去演变3种,此时9种,将9种状态存起来,同时可能出现重复的状态,就去重,再分别与s2比较
* 4. 用到队列: 可以从队首依次往后读,并将此元素删除,读的过程中与s2进行比较,若不相等,把它的变种给生成出来,同时判断之前的状态中是否已经出现过
* 5. 用到Set, 用来保存各种串状态*/
public class T9 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String strStart = input.next();
String strEnd = input.next();
jump(strStart, strEnd);
}
//青蛙跳跃的函数
public static void jump(String strStart,String strEnd) {
Queue<MyNode> queue = new LinkedList<>();//队列
Set<String> set = new TreeSet<String>(); //保存每个串的历史记录
queue.add(new MyNode(strStart, 0));//第一个字符串加入队列
set.add(strStart);//第一个字符串加入历史记录
while(queue.size()!=0) {//循环队列 队列读取队首元素,并将此元素删除
MyNode myNode = queue.poll();//读取第一个,并将其从队列中删掉
if(myNode.str.equals(strEnd)) {
System.out.println(myNode.step);
break;
}
//青蛙要跳跃,字符串要变化:让当前的串生成变种到队列中去 变种的生成:就是字符数组中的字符进行交换:找到星号,星号减1,减2,减3,加1,加2,加3
char[] arrCh = myNode.str.toCharArray();
int index = myNode.str.indexOf("*");//找到星号的下标(空杯子的位置),
int start = index -3;//与星号交换的开始位置
int end = index +3; //与星号交换的结束位置
//同时,要考虑星号的位置若太靠左或靠右的话,就不可减或加上3了 就是星号开始和结束位置的临界位置判断
if(start<=0) {
start = 0;
}
if(end>=arrCh.length-1) {
end = arrCh.length -1;
}
for(int i=start;i<=end;i++) {
if(arrCh[i]=='*') continue; //就是*自己和自己不需要交换
//若串s生成变种1得到串s1,需要还原为s,再生成变种2得到串s2 生成变种的操作可以封装为一个方法
swap(arrCh, i, index);//交换
//生成变种,将其放到队列中去,同时判断在之前的状态中是否已经出现过
if(set.contains(String.valueOf(arrCh)) == false) {
queue.add(new MyNode(String.valueOf(arrCh), myNode.step+1));
set.add(String.valueOf(arrCh));
}
swap(arrCh, i, index);//还原
}
}
}
//生成变种的操作可以封装为一个方法
public static void swap(char[] arrCh,int i,int j) {
char t = arrCh[i];
arrCh[i] = arrCh[j];
arrCh[j] = t;
}
}
class MyNode {
String str;//
int step;// 变种的字符串是青蛙跳了几步所形成的此状态
public MyNode(String str, int step) {
this.str = str;
this.step = step;
}
}