迷宫
X星球的一处迷宫游乐场建在某个小山坡上。
它是由10x10相互连通的小房间组成的。
房间的地板上写着一个很大的字母。
我们假设玩家是面朝上坡的方向站立,则:
L表示走到左边的房间,
R表示走到右边的房间,
U表示走到上坡方向的房间,
D表示走到下坡方向的房间。
X星球的居民有点懒,不愿意费力思考。
他们更喜欢玩运气类的游戏。这个游戏也是如此!
开始的时候,直升机把100名玩家放入一个个小房间内。
玩家一定要按照地上的字母移动。
迷宫地图如下:
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
请你计算一下,最后,有多少玩家会走出迷宫?
而不是在里边兜圈子。
请提交该整数,表示走出迷宫的玩家数目,不要填写任何多余的内容。
如果你还没明白游戏规则,可以参看一个简化的4x4迷宫的解说图:
p1.png
思路:dfs+标记深搜
public class Main {
static String[] data = new String[10];
static int[][] vis = new int[10][10];
static int count = 0;
public static void main(String[] args) {
data[0] = "UDDLUULRUL";
data[1] = "UURLLLRRRU";
data[2] = "RRUURLDLRD";
data[3] = "RUDDDDUUUU";
data[4] = "URUDLLRRUU";
data[5] = "DURLRLDLRL";
data[6] = "ULLURLLRDU";
data[7] = "RDLULLRDDD";
data[8] = "UUDDUDUDLL";
data[9] = "ULRDLUURRR";
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
clear();
boolean res = dfs(i, j);
if (res) {
count++;
}
}
}
System.out.println(count);
}
public static void clear() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
vis[i][j] = 0;
}
}
}
public static boolean dfs(int i, int j) {
if (i < 0 || j < 0 || i > 9 || j > 9) {
return true;
}
if (vis[i][j] == 1) {
return false;
}
vis[i][j] = 1;
switch (data[i].charAt(j)) {
case 'U':
return dfs(i - 1, j);
case 'D':
return dfs(i + 1, j);
case 'L':
return dfs(i, j - 1);
case 'R':
return dfs(i, j + 1);
default:
return false;
}
}
}
9数算式
观察如下的算式:
9213 x 85674 = 789314562
左边的乘数和被乘数正好用到了1~9的所有数字,每个1次。
而乘积恰好也是用到了1~9的所有数字,并且每个1次。
请你借助计算机的强大计算能力,找出满足如上要求的9数算式一共有多少个?
注意:
总数目包含题目给出的那个示例。
乘数和被乘数交换后作为同一方案来看待。
import java.util.HashSet;
import java.util.Set;
public class Main2 {
static int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
static int res = 0;
public static void main(String[] args) {
dfs(0);
System.out.println(res / 2);
}
public static void dfs(int i) {
if (i == 9) {
for (int j = 1; j < 9; j++) {
int x1 = a2j(0, j);
int x2 = a2j(j, 9);
int x = x1 * x2;
if (check(x)) {
res++;
}
}
}
for (int j = i; j < 9; j++) {
int t = a[i];
a[i] = a[j];
a[j] = t;
dfs(i + 1);
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
public static boolean check(int i) {
String s = i + "";
if (s.length() != 9 || s.indexOf('0') > -1)
return false;
Set<Character> set = new HashSet<>();
for (int j = 0; j < s.length(); j++) {
set.add(s.charAt(j));
}
return set.size() == 9;
}
public static int a2j(int i, int j) {
int ans = 0;
int p = 1;
for (int k = j - 1; k >= i; k--) {
ans += a[k] * p;
p *= 10;
}
return ans;
}
}
方格分割
6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。
如图:p1.png, p2.png, p3.png 就是可行的分割法。
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。
请提交该整数,不要填写任何多余的内容或说明文字。
思路:可以把中心点作为起点,利用dfs深搜,两个人走着完全对称的路径,若都能走到边界则总方案数加1,由于是中心对称图形,所以最终得到的结果除以4
public class Main3 {
// 标记数组
static int[][] vis = new int[7][7];
// 方向数组
static int[][] dir = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
static int ans = 0;
public static void main(String[] args) {
// 中心点
vis[3][3] = 1;
// 从中心点出发
dfs(3, 3);
// 旋转的情况是一样的,需要除以4
System.out.println(ans / 4);
}
public static void dfs(int x, int y) {
// 走到边界
if (x == 0 || y == 0 || x == 6 || y == 6) {
ans++;
return;
}
for (int i = 0; i < 4; i++) {
int dx = x + dir[i][0];
int dy = y + dir[i][1];
if (vis[dx][dy] == 1)
continue;
// 标记走过
vis[dx][dy] = 1;
// 对称点
vis[6 - dx][6 - dy] = 1;
dfs(dx, dy);
// 回溯
vis[dx][dy] = 0;
vis[6 - dx][6 - dy] = 0;
}
}
}
正则问题
考虑一种简单的正则表达式:
只由 x ( ) | 组成的正则表达式。
小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是6。
输入
一个由x()|组成的正则表达式。输入长度不超过100,保证合法。
输出
这个正则表达式能接受的最长字符串的长度。
例如,
输入:
((xx|xxx)x|(x|xx))xx
程序应该输出:
6
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
import java.util.Scanner;
public class Main4 {
static String s;
static int pos = 0;// 指针
static int len = 0;// 长度
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
s = sc.next();
len = s.length();
System.out.println(f());
}
public static int f() {
int temp = 0;// 临时变量
int m = 0;// 记录最大值
while (pos < len) {
if (s.charAt(pos) == '(') {
pos++;
// 递归
temp += f();
} else if (s.charAt(pos) == 'x') {
pos++;
temp++;
} else if (s.charAt(pos) == '|') {
pos++;
m = Math.max(m, temp);
temp = 0;
} else if (s.charAt(pos) == ')') {
pos++;
m = Math.max(m, temp);
return m;
}
}
m = Math.max(m, temp);
return m;
}
}
包子凑数
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。
例如,
输入:
2
4
5
程序应该输出:
6
再例如,
输入:
2
4
6
程序应该输出:
INF
样例解释:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
import java.util.Scanner;
public class Main5 {
// f[i]表示是否能凑出i
/*
* 欧几里得扩展公式不定方程系数互质时最大凑不出的值满足 x*y-x-y,这里是100*100-200
*/
static boolean[] f = new boolean[10000];
static int g;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n + 1];
f[0] = true;
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
// 求最大公约数
if (i == 1)
g = a[i];
else
g = gcd(g, a[i]);
// 完全背包的递推
for (int j = 0; j < 10000 - a[i]; j++) {
if (f[j]) {
f[j + a[i]] = true;
}
}
}
// 系数不互质
if (g != 1) {
System.out.println("INF");
return;
}
// 统计递推表里凑不出的个数
int ans = 0;
for (int i = 0; i < 10000; i++) {
if (!f[i]) {
ans++;
}
}
System.out.println(ans);
}
public static int gcd(int a, int b) {
if (b == 0)
return a;
return gcd(b, a % b);
}
}
分巧克力
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。
输出
输出切出的正方形巧克力最大可能的边长。
样例输入:
2 10
6 5
5 6
样例输出:
2
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
import java.util.Scanner;
public class Main6 {
static int n, k;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
int[] h = new int[n];
int[] w = new int[n];
for (int i = 0; i < n; i++) {
h[i] = sc.nextInt();
w[i] = sc.nextInt();
}
// 最小和最大边长
int l = 1;
int r = 100001;
int ans = 0;
while (l <= r) {
int mid = l + ((r - l) >> 1);
int cnt = 0;
for (int i = 0; i < n; i++) {
cnt += (h[i] / mid) * (w[i] / mid);
}
if (cnt >= k) {
l = mid + 1;
ans = mid;
} else {
r = mid - 1;
}
}
System.out.println(ans);
}
}
油漆面积
X星球的一批考古机器人正在一片废墟上考古。
该区域的地面坚硬如石、平整如镜。
管理人员为方便,建立了标准的直角坐标系。
每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同。
经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。
矩形的表示格式为(x1,y1,x2,y2),代表矩形的两个对角点坐标。
为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。
小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。
其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。
注意,各个矩形间可能重叠。
本题的输入为若干矩形,要求输出其覆盖的总面积。
输入格式:
第一行,一个整数n,表示有多少个矩形(1<=n<10000)
接下来的n行,每行有4个整数x1 y1 x2 y2,空格分开,表示矩形的两个对角顶点坐标。
(0<= x1,y1,x2,y2 <=10000)
输出格式:
一行一个整数,表示矩形覆盖的总面积。
例如,
输入:
3
1 5 10 10
3 1 20 20
2 7 15 17
程序应该输出:
340
再例如,
输入:
3
5 2 10 6
2 7 12 10
8 1 15 15
程序应该输出:
128
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
import java.util.Scanner;
public class Main7 {
// 标记数组
static boolean[][] vis = new boolean[10005][10005];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for (int i = 0; i < n; i++) {
int x1 = sc.nextInt();
int y1 = sc.nextInt();
int x2 = sc.nextInt();
int y2 = sc.nextInt();
paint(x1, y1, x2, y2);
}
int sum = 0;
for (int i = 0; i < 10005; i++) {
for (int j = 0; j < 10005; j++) {
if (vis[i][j]) {
sum += 1;
}
}
}
System.out.println(sum);
}
public static void paint(int x1, int y1, int x2, int y2) {
for (int i = x1; i < x2; i++) {
for (int j = y1; j < y2; j++) {
vis[i][j] = true;
}
}
}
}
纸牌三角形
A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算)。要求每个边的和相等。
下图就是一种排法
在这里插入图片描述
这样的排法可能会有很多。
如果考虑旋转、镜像后相同的算同一种,一共有多少种不同的排法呢?
请你计算并提交该数字。
注意:需要提交的是一个整数,不要提交任何多余内容。
//旋转要除以3,镜像要除以2
public class Main8 {
static int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
static int ans = 0;
public static void main(String[] args) {
f(0);
System.out.println(ans / 6);
}
public static void f(int i) {
if (i == a.length) {
if (check(a)) {
ans++;
}
return;
}
for (int j = i; j < a.length; j++) {
int t = a[i];
a[i] = a[j];
a[j] = t;
f(i + 1);
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
public static boolean check(int[] a) {
int a1 = a[0] + a[1] + a[3] + a[5];
int a2 = a[0] + a[2] + a[4] + a[8];
int a3 = a[5] + a[6] + a[7] + a[8];
if (a1 == a2 && a2 == a3) {
return true;
}
return false;
}
}
标题:承压计算
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
请你推算出:读数最大的电子秤的示数为多少?
注意:需要提交的是一个整数,不要填写任何多余的内容。
import java.util.Arrays;
import java.util.Scanner;
public class Main9 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long[][] a = new long[30][30];
long f = (long) Math.pow(2, 30);
for (int i = 0; i < 29; i++) {
for (int j = 0; j <= i; j++) {
a[i][j] = sc.nextLong();
a[i][j] *= f;
}
}
for (int i = 0; i < 29; i++) {
for (int j = 0; j <= i; j++) {
long tmp = a[i][j] / 2;
a[i + 1][j] += tmp;
a[i + 1][j + 1] += tmp;
}
}
Arrays.sort(a[29]);
System.out.println(a[29][0] / 2);
System.out.println(a[29][29] / 2);
}
}
日期问题
小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在1960年1月1日至2059年12月31日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。
比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?
输入
一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)
输入
输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。多个日期按从早到晚排列。
样例输入
02/03/04
样例输出
2002-03-04
2004-02-03
2004-03-02
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
不要使用package语句。不要使用jdk1.7及以上版本的特性。
主类的名字必须是:Main,否则按无效代码处理。
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
public class Main10 {
static String s;
static String[] c;
static Set<String> set = new TreeSet<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
s = sc.next();
int a = (s.charAt(0) - '0') * 10 + (s.charAt(1) - '0');
int b = (s.charAt(3) - '0') * 10 + (s.charAt(4) - '0');
int c = (s.charAt(6) - '0') * 10 + (s.charAt(7) - '0');
check1(a, b, c);
check1(c, a, b);
check1(c, b, a);
for (String t : set) {
System.out.println(t);
}
}
public static void check1(int a, int b, int c) {
if (a >= 0 && a < 60) {
a += 2000;
} else if (a >= 60 && a <= 99) {
a += 1900;
} else
return;
if (b < 1 || b > 12) {
return;
}
if (c < 1 || c > 31) {
return;
}
switch (b) {
case 2:
if (isR(a)) {
if (c > 29) {
return;
}
} else {
if (c > 28) {
return;
}
}
break;
case 4:
case 6:
case 9:
case 11:
if (c > 30)
return;
break;
default:
break;
}
String s1 = "" + a;
String s2 = "" + b;
String s3 = "" + c;
if (s2.length() == 1)
s2 = "0" + s2;
if (s3.length() == 1)
s3 = "0" + s3;
set.add(s1 + "-" + s2 + "-" + s3);
}
public static boolean isR(int a) {
return (a % 4 == 0 && a % 100 != 0) || (a % 400 == 0);
}
}
k倍区间
给定一个长度为N的数列,A1, A2, … AN,如果其中一段连续的子序列Ai, Ai+1, … Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出
输出一个整数,代表K倍区间的数目。
例如,
输入:
5 2
1
2
3
4
5
程序应该输出:
6
利用前缀和
import java.util.Scanner;
public class Main11 {
static int n, k;
static int[] a;
static int[] s;
static long ans = 0;
static int[] cnt = new int[1000001];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
a = new int[n + 1];
s = new int[n + 1];
s[0] = 0;
// 余数为0的前缀和有一个了
cnt[0] = 1;
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
// 求前缀和
s[i] = s[i - 1] + a[i];
// 求前缀和取模k的余数
s[i] %= k;
cnt[s[i]]++;
}
// 余数为0~k-1之间
for (int i = 0; i < k; i++) {
// 余数相等的前缀和中任意取两个
ans += (long) cnt[i] * (cnt[i] - 1) / 2;
}
System.out.println(ans);
}
}
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,否则按无效代码处理。
思路:利用进制转换的思想
import java.util.Scanner;
public class Main12 {
static long n;
// 记录余数
static long[] ans = new long[100];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextLong();
int cnt = 0;// 余数个数
while (n != 0) {
// 特殊处理
if (n % 26 == 0) {
ans[cnt++] = 26;
n = n / 26 - 1;
} else {
ans[cnt++] = n % 26;
n /= 26;
}
}
for (int i = cnt - 1; i >= 0; i--) {
System.out.print((char) ('A' + ans[i] - 1));
}
}
}
拉马车
题目描述
小的时候,你玩过纸牌游戏吗?
有一种叫做“拉马车”的游戏,规则很简单,却很吸引小朋友。
其规则简述如下:
假设参加游戏的小朋友是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
import java.util.Scanner;
public class Main13 {
static StringBuilder A;
static StringBuilder B;
static StringBuilder C;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String a = sc.next();
String b = sc.next();
A = new StringBuilder(a);
B = new StringBuilder(b);
C = new StringBuilder();
boolean flagA = true;// A出牌标志
boolean flagB = false;// B出牌标志
while (true) {
// A出牌
if (flagA) {
flagA = op(A, C);
if (A.length() == 0) {
System.out.println(B);
break;
}
flagB = !flagA;
}
// B出牌
if (flagB) {
flagB = op(B, C);
if (B.length() == 0) {
System.out.println(A);
break;
}
flagA = !flagB;
}
}
}
public static boolean op(StringBuilder x, StringBuilder t) {
if (x.length() == 0)
return false;
boolean ans = true;
char front = x.charAt(0);// 取第一个字符
int i = t.indexOf(front + "");
if (i != -1) {// 桌上有相同的牌
/*
* 将这张牌插入桌上牌x的尾部,并将桌上 0~i之间的牌依次插入x的尾部
*/
x.append(front);
for (int j = 0; j <= i; j++) {
x.append(t.charAt(j));
}
// 从桌面上移除这些牌
t.delete(0, i + 1);
} else {
// 将牌放在上面,下标为0
t.insert(0, front);
ans = false;
}
x.deleteCharAt(0);// 删除第一个字符
return ans;
}
}
问题描述
X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。
*WWWBBB
其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。
X星的青蛙很有些癖好,它们只做3个动作之一:
1. 跳到相邻的空杯子里。
2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。
对于上图的局面,只要1步,就可跳成下图局面:
WWW*BBB
本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。
输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。
样例输入
WWBB
WWBB
样例输出
2
样例输入
WWWBBB
BBBWWW
样例输出
10
数据规模和约定
我们约定,输入的串的长度不超过15
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
思路:bfs
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
public class Main14 {
// 封装状态和层次
private static class StateAndLevel {
StringBuilder state;
int level;
int pos;
public StateAndLevel(StringBuilder state, int level, int pos) {
this.state = state;
this.level = level;
this.pos = pos;
}
}
private static StringBuilder start;
private static StringBuilder target;
static Set<String> allState = new HashSet<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
start = new StringBuilder(sc.next());
target = new StringBuilder(sc.next());
bfs();
}
public static void bfs() {
// 将初始状态加入队列
Queue<StateAndLevel> q = new LinkedList<StateAndLevel>();
q.add(new StateAndLevel(start, 0, start.indexOf("*")));
allState.add(start.toString());
/*
* 不停弹出队列,一步演化 成相邻状态,判重后加入队列
*/
while (!q.isEmpty()) {
StateAndLevel front = q.poll();
StringBuilder state = front.state;
int level = front.level;
// 和目标值做比对,这里注意要转成String
if (state.toString().equals(target.toString())) {
System.out.println(level);
break;
}
int pos = front.pos;
// 演化出若干个状态
for (int i = -3; i <= 3; i++) {
if (i == 0)
continue;
if (pos + i >= 0 && pos + i < state.length()) {
StringBuilder new_state = new StringBuilder(state);
// 交换得到新状态
swap(new_state, pos, pos + i);
// 判重
if (!allState.contains(new_state.toString())) {
q.add(new StateAndLevel(new_state, level + 1, pos + i));
allState.add(new_state.toString());
}
}
}
}
}
public static void swap(StringBuilder new_state, int i, int j) {
char t = new_state.charAt(i);
new_state.setCharAt(i, new_state.charAt(j));
new_state.setCharAt(j, t);
}
}