结果填空
购物单
小明刚刚找到工作,老板人很好,只是老板夫人很爱购物。老板忙的时候经常让小明帮忙到商场代为购物。小明很厌烦,但又不好推辞。
这不,XX大促销又来了!老板夫人开出了长长的购物单,都是有打折优惠的。
小明也有个怪癖,不到万不得已,从不刷卡,直接现金搞定。
现在小明很心烦,请你帮他计算一下,需要从取款机上取多少现金,才能搞定这次购物。
取款机只能提供100元面额的纸币。小明想尽可能少取些现金,够用就行了。
你的任务是计算出,小明最少需要取多少现金。
以下是让人头疼的购物单,为了保护隐私,物品名称被隐藏了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| **** 180.90 88折 **** 10.25 65折 **** 56.14 9折 **** 104.65 9折 **** 100.30 88折 **** 297.15 半价 **** 26.75 65折 **** 130.62 半价 **** 240.28 58折 **** 270.62 8折 **** 115.87 88折 **** 247.34 95折 **** 73.21 9折 **** 101.00 半价 **** 79.54 半价 **** 278.44 7折 **** 199.26 半价 **** 12.97 9折 **** 166.30 78折 **** 125.50 58折 **** 84.98 9折 **** 113.35 68折 **** 166.57 半价 **** 42.56 9折 **** 81.90 95折 **** 131.78 8折 **** 255.89 78折 **** 109.17 9折 **** 146.69 68折 **** 139.33 65折 **** 141.16 78折 **** 154.74 8折 **** 59.42 8折 **** 85.44 68折 **** 293.70 88折 **** 261.79 65折 **** 11.30 88折 **** 268.27 58折 **** 128.29 88折 **** 251.03 8折 **** 208.39 75折 **** 128.88 75折 **** 62.06 9折 **** 225.87 75折 **** 12.89 75折 **** 34.28 75折 **** 62.16 58折 **** 129.12 半价 **** 218.37 半价 **** 289.69 8折
|
需要说明的是,88折指的是按标价的88%计算,而8折是按80%计算,余者类推。
特别地,半价是按50%计算。
请提交小明要从取款机上提取的金额,单位是元。 5200
答案是一个整数,类似4300的样子,结尾必然是00,不要填写任何多余的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.util.ArrayList; import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in));
public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); while (scanner.hasNext()) { list.add(scanner.next()); } int sum = 0; for (int i = 0; i < list.size(); i++) { if (i % 3 == 1) { double percent = 0.0; if (list.get(i + 1).equals("半价")) { percent = 0.5; } else { percent = Integer.parseInt(list.get(i + 1).split("折")[0]) / 10.0; if (percent > 0) { percent /= 10.0; } } sum += Double.parseDouble(list.get(i)) * percent; } } System.out.println(sum); } }
|
纸牌三角形
A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算)。要求每个边的和相等。
下图就是一种排法。
这样的排法可能会有很多。
如果考虑旋转、镜像后相同的算同一种,一共有多少种不同的排法呢? 144
回溯1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class Main { static int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; static int solution = 0;
public static void main(String[] args) { backTrack(0); System.out.println(solution / 6); }
static void backTrack(int num) { if (num == 9) { if (isOK()) { solution++; } } else { for (int i = num; i < 9; i++) { swap(num, i); backTrack(num + 1); swap(num, i); } } }
static void swap(int i, int j) { int tmp = array[i]; array[i] = array[j]; array[j] = tmp; }
static boolean isOK() { int a = array[0] + array[1] + array[2] + array[3]; int b = array[3] + array[4] + array[5] + array[6]; int c = array[6] + array[7] + array[8] + array[0]; return a == b && a == c; } }
|
等差素数列
2,3,5,7,11,13,….是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。
2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。这是数论领域一项惊人的成果!
有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:
长度为10的等差素数列,其公差最小值是多少? 210
分别是:199 409 619 829 1039 1249 1459 1669 1879 2089
暴力1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| import java.util.ArrayList; import java.util.LinkedList; import java.util.List;
public class Main { static ArrayList<Integer> prime;
public static void main(String[] args) { prime = getPrime(10000); find(10); System.out.println(); }
static void find(int length) { int tolerance = 1; int i = 0; ArrayList<Integer> container = new ArrayList<>(); int nums = 1; while (true) { container.add(prime.get(i)); for (int k = i + 1, j = 1; k < prime.size(); k++) { if (prime.get(k) - prime.get(i) == j * tolerance) { container.add(prime.get(k)); nums++; j++; } } if (nums == 10) { for (Integer k : container) { System.out.print(k + " "); } System.out.println(); System.out.println(tolerance); break; } else { container.clear(); nums = 1; i++; if (i == prime.size()) { tolerance++; i = 0; } } } }
static ArrayList<Integer> getPrime(int n) { boolean[] notPrime = new boolean[n + 1]; int sqrtN = (int) Math.sqrt(n); for (int i = 2; i <= sqrtN; i++) { if (notPrime[i]) continue; for (int j = i * i; j <= n; j += i) { notPrime[j] = true; } } ArrayList<Integer> prime = new ArrayList<>(); if (n > 1) { prime.add(2); } for (int i = 3; i <= n; i += 2) { if (!notPrime[i]) { prime.add(i); } } System.out.println(prime.size()); return prime; } }
|
方格分割
6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。
如图是可行的分割法:
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。 509
注意:旋转对称的属于同一种分割法。
分析:
图形中心对称,从中间走,能走到边界的话即是一种分割方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class Main { static int n = 6; static int[][] map = new int[n + 1][n + 1]; static int solution = 0; static int[][] direction = new int[][] { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
public static void main(String[] args) { map[3][3] = 1; backTrack(3, 3); System.out.println(solution / 4); }
static void backTrack(int x, int y) { if (x == 0 || y == 0 || x == n || y == n) { solution++; return; } for (int i = 0; i < 4; i++) { int dx = x + direction[i][0]; int dy = y + direction[i][1]; if (map[dx][dy] == 0) { map[dx][dy] = 1; map[n - dx][n - dy] = 1; backTrack(dx, dy); map[dx][dy] = 0; map[n - dx][n - dy] = 0; } } } }
|
承压计算
X星球的高科技实验室中整齐地堆放着某批珍贵金属原料。
每块金属原料的外形、尺寸完全一致,但重量不同。
金属材料被严格地堆放成金字塔形。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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
请你推算出:读数最大的电子秤的示数为多少? 72665192664
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in));
public static void main(String[] args) { double[][] weight = new double[30][30]; for (int i = 0; i < 29; i++) { for (int j = 0; j <= i; j++) { weight[i][j] = scanner.nextDouble(); } } for (int i = 1; i < 30; i++) { for (int j = 0; j < i; j++) { double front = weight[i - 1][j] / 2.0; weight[i][j] += front; weight[i][j + 1] += front; } } double max = Double.MIN_VALUE; double min = Double.MAX_VALUE; for (int i = 0; i < 30; i++) { max = Math.max(weight[29][i], max); min = Math.max(weight[29][i], min); } System.out.println(2086458231 / min * max); } }
|
魔方状态
二阶魔方就是只有2层的魔方,只由8个小块组成。
小明很淘气,他只喜欢3种颜色,所有把家里的二阶魔方重新涂了颜色,如下:
前面:橙色
右面:绿色
上面:黄色
左面:绿色
下面:橙色
后面:黄色
请你计算一下,这样的魔方被打乱后,一共有多少种不同的状态。
如果两个状态经过魔方的整体旋转后,各个面的颜色都一致,则认为是同一状态。
迷宫
X星球的一处迷宫游乐场建在某个小山坡上。
它是由10x10相互连通的小房间组成的。
房间的地板上写着一个很大的字母。
我们假设玩家是面朝上坡的方向站立,则:
L表示走到左边的房间,
R表示走到右边的房间,
U表示走到上坡方向的房间,
D表示走到下坡方向的房间。
X星球的居民有点懒,不愿意费力思考。
他们更喜欢玩运气类的游戏。这个游戏也是如此!
开始的时候,直升机把100名玩家放入一个个小房间内。
玩家一定要按照地上的字母移动。
迷宫地图如下:
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
请你计算一下,最后,有多少玩家会走出迷宫而不是在里边兜圈子? 31
如果你还没明白游戏规则,可以参看一个简化的4x4迷宫的解说图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in)); static int n = 10; static char[][] map = new char[n][n]; static int people = 0;
public static void main(String[] args) { for (int i = 0; i < n; i++) { String buf = scanner.next(); char[] c = buf.toCharArray(); for (int j = 0; j < n; j++) { map[i][j] = c[j]; } } goMap(); System.out.println(people); }
static void goMap() { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { go(i, j); } } }
static void go(int x, int y) { for (int i = 1; i <= n * n; i++) { switch (map[x][y]) { case 'U': x--; break; case 'D': x++; break; case 'L': y--; break; case 'R': y++; break; } if (x == -1 || x == 10 || y == -1 || y == 10) { people++; break; } } } }
|
9数算式
观察如下的算式:
9213 x 85674 = 789314562
左边的乘数和被乘数正好用到了19的所有数字,每个1次。
而乘积恰好也是用到了19的所有数字,并且每个1次。
请你借助计算机的强大计算能力,找出满足如上要求的9数算式一共有多少个? 346
注意:
- 总数目包含题目给出的那个示例。
- 乘数和被乘数交换后作为同一方案来看待。
回溯1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class Main { static int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; static int count = 0;
public static void main(String args[]) { backTrack(0); System.out.println(count); }
static void backTrack(int n) { if (n == 9) { if (isOK()) { count++; } } else { for (int i = n; i < 9; i++) { swap(i, n); backTrack(n + 1); swap(i, n); } } }
static boolean isOK() { long a = array[0] * 1000 + array[1] * 100 + array[2] * 10 + array[3]; long b = array[4] * 10000 + array[5] * 1000 + array[6] * 100 + array[7] * 10 + array[8]; long c = a * b; return isVisistOnce(c); }
static boolean isVisistOnce(long n) { boolean[] num = new boolean[10]; while (n != 0) { int k = (int) (n % 10); if (num[k] || k == 0) { return false; } num[k] = true; n /= 10; } for (int i = 1; i <= 9; i++) { if (!num[i]) { return false; } } return true; }
static void swap(int i, int j) { int tmp = array[i]; array[i] = array[j]; array[j] = tmp; } }
|
跳蚱蜢
有 9 只盘子,排成 1 个圆圈。
其中 8 只盘子内装着 8 只蚱蜢,有一个是空盘。
我们把这些蚱蜢顺时针编号为 1~8
每只蚱蜢都可以跳到相邻的空盘中,也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。
请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列,并且保持空盘的位置不变(也就是 1-8 换位,2-7 换位,…),至少要经过多少次跳跃?20
分析:
把盘子当成四叉树,把空盘子当成起点,问题就转换成了从 12345678 状态开始,到 87654321 状态结束,最短步数是多少?
广度搜索1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| import java.util.ArrayDeque; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue;
class Main { static String start = "12345678", end = "87654321"; static Map<String, Boolean> isVisit = new HashMap<>();
public static void main(String[] args) { bfs(); }
static void bfs() { isVisit.put(start, true); Queue<Node> queue = new ArrayDeque<>(); queue.offer(new Node(start, 0)); while (!queue.isEmpty()) { Node headNode = queue.poll(); headNode.buildChildNodes(); for (Node node : headNode.childNode) { System.out.println(node.value + " " + node.step); if (node.value.equals(end)) { System.out.println(node.step); return; } else { queue.offer(node); } } } }
static class Node { List<Node> childNode; String value; int step;
Node(String value, int step) { this.value = value; this.step = step; }
void buildChildNodes() { this.childNode = new LinkedList<>();
List<String> array = new LinkedList<>(); array.add(this.value.substring(1) + "" + this.value.charAt(0)); array.add(this.value.substring(2) + "" + this.value.charAt(1) + "" + this.value.charAt(0)); array.add(this.value.charAt(this.value.length() - 1) + "" + this.value.substring(this.value.length() - 1)); array.add(this.value.charAt(this.value.length() - 1) + "" + this.value.charAt(this.value.length() - 2) + "" + this.value.substring(0, this.value.length() - 2));
for (int i = 0; i < array.size(); i++) { if (isVisit.get(array.get(i)) == null && array.get(i).length() == 8) { this.childNode.add(new Node(array.get(i), this.step + 1)); isVisit.put(array.get(i), true); } } } } }
|
算式900
小明的作业本上有道思考题:
看下面的算式:
(□□□□ - □□□□) * □□ = 900
其中的小方块代表 09 的数字,这 10 个方块刚好包含了 09 中的所有数字。
注意:0 不能作为某个数字的首位。
小明经过几天的努力,终于做出了答案!如下:
(5012 - 4987) * 36 = 900
用计算机搜索后,发现还有另外一个解,本题的任务就是:请你算出这另外的一个解。
6048 5973 12
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class Main { static int[] number = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; public static void main(String[] args) { backTrack(0); }
static void backTrack(int n) { if (n == 10) { int a = number[0] * 1000 + number[1] * 100 + number[2] * 10 + number[3]; int b = number[4] * 1000 + number[5] * 100 + number[6] * 10 + number[7]; int c = number[8] * 10 + number[9]; if ((a - b) * c == 900) { System.out.println(a + " " + b + " " + c); } } else { for (int i = n; i < 10; i++) { swap(n, i); if (number[0] != 0 && number[4] != 0 && number[8] != 0) { backTrack(n + 1); } swap(n, i); } } }
static void swap(int i, int j) { int tmp = number[i]; number[i] = number[j]; number[j] = tmp; } }
|
外星日历
某星系深处发现了文明遗迹。
他们的计数也是用十进制。
他们的文明也有日历。日历只有天数,没有年、月的概念。
有趣的是,他们也使用了类似“星期”的概念,只不过他们的一个星期包含了 9 天,为了方便,这里分别记为: A, B, C….H, I
从一些资料上看到:
他们的 23 日是星期 E
他们的 190 日是星期 A
他们的 343251 日是星期 I
令人兴奋的是,他们居然也预见了“世界末日”的那天,当然是一个很大很大的数字 651764141421415346185
请你计算一下,这遥远的一天是该文明的星期几? G
方法1:windows 的计算器直接 651764141421415346185 mod 9 = 7
方法2:
1 2 3
| BigInteger a = new BigInteger("651764141421415346185"); BigInteger b = new BigInteger("9"); System.out.println(a.mod(b));
|
代码填空
取数位
求 1 个整数的第 k 位数字有很多种方法,以下的方法就是一种。
对于题目中的测试数据,应该打印5。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Main { static int len(int x) { if (x < 10) return 1; return len(x / 10) + 1; }
static int f(int x, int k) { if (len(x) - k == 0) return x % 10; return f(x / 10, k); }
public static void main(String[] args) { int x = 23513; System.out.println(f(x, 3)); } }
|
最大公共子串
最大公共子串长度问题就是:求两个串的所有子串中能够匹配上的最大长度是多少。
比如:”abcdkkk” 和 “baabcdadabc”,可以找到的最长的公共子串是”abcd”,所以最大公共子串长度为4。
下面的程序是采用矩阵法进行求解的,这对串的规模不大的情况还是比较有效的解法。
请分析该解法的思路,并补全划线部分缺失的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class Main { 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]; } } }
return max; }
public static void main(String[] args) { int n = f("abcdkkk", "baabcdadabc"); System.out.println(n); } }
|
动态规划:
1 2 3 4 5 6 7 8 9 10 11 12
| 通过前一过程的结果得到当前状态的结果 a[i][j] = a[i - 1][j - 1] + 1
b, a, a, b, c, d, a, d, a, b, c [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] a [0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0] b [0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0] c [0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3] d [0, 0, 0, 0, 0, 0, 4, 0, 1, 0, 0, 0] k [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] k [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] k [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
字母组串
由 A, B, C 这3个字母就可以组成许多串。
比如:”A”, “AB”, “ABC”, “ABA”, “AACBB”….
现在,小明正在思考一个问题:如果每个字母的个数有限定,能组成多少个已知长度的串呢?
他请好朋友来帮忙,很快得到了代码,解决方案超级简单,然而最重要的部分却语焉不详。
请仔细分析源码,填写划线部分缺少的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class A { static int f(int a, int b, int c, int n) { if(a<0 || b<0 || c<0) return 0; if(n==0) return 1; return f(a - 1, b, c, n - 1) + f(a, b - 1, c, n - 1) + f(a, b, c - 1, n - 1); } public static void main(String[] args) { System.out.println(f(1,1,1,2)); System.out.println(f(1,2,3,3)); } }
|
对于上面的测试数据,小明口算的结果应该是:
6
19
杨辉三角
杨辉三角也叫帕斯卡三角,在很多数量关系中可以看到,十分重要。
第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
直观地看,需要开辟一个二维数组,其实一维数组也可以胜任。
如下程序就是用一维数组“腾挪”的解法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class Main { public static void main(String[] args) { System.out.println(f(6, 2)); System.out.println(f(6, 3)); System.out.println(f(40, 20)); }
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[1024]; a[0] = 1; a[1] = 1; int p = 2; int q;
while (p <= row) { a[p] = 1; for (q = p - 1; q > 0; q--) a[q] = a[q] + a[q - 1]; p++; } return a[col]; } }
|
程序设计
日期问题
小明正在整理一批历史文献。这些历史文献中出现了很多日期。
小明知道这些日期都在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
直接列举+Calendar判断1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| import java.util.Arrays; import java.util.Calendar; import java.util.LinkedHashSet; import java.util.Scanner; import java.io.BufferedInputStream;
class Main { static LinkedHashSet<String> set = new LinkedHashSet<>(); static Scanner scanner = new Scanner(new BufferedInputStream(System.in));
public static void main(String args[]) { String string = scanner.next(); String[] strNum = string.split("/"); int[] num = { Integer.valueOf(strNum[0]), Integer.valueOf(strNum[1]), Integer.valueOf(strNum[2]) }; Arrays.sort(num); combine(num[0], num[1], num[2]); combine(num[0], num[2], num[1]); combine(num[1], num[0], num[2]); combine(num[1], num[2], num[0]); combine(num[2], num[0], num[1]); combine(num[2], num[1], num[0]); set.stream().forEach(x -> System.out.println(x)); }
static void combine(int a, int b, int c) { int year; if (0 <= a && a <= 59) { year = 2000 + a; } else if (60 <= a && a <= 99) { year = 1900 + a; } else { return; } if (isValidate(year, b, c)) { String month = String.format("%02d", b); String date = String.format("%02d", c); set.add(year + "-" + month + "-" + date); } }
static boolean isValidate(int year, int month, int date) { try { Calendar calendar = Calendar.getInstance(); calendar.setLenient(false); calendar.set(year, month - 1, date); calendar.get(Calendar.YEAR); return true; } catch (Exception e) { return false; } } }
|
包子凑数
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有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。
样例输入1:
2
4
5
输出:
6
样例输入2:
2
4
6
输出:
INF
样例说明:
对于样例1,凑不出的数目包括:1, 2, 3, 6, 7, 11。
对于样例2,所有奇数都凑不出来,所以有无限多个。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
分析:
完全背包
扩展欧几里德,方程 ax + by = gcd(a,b)
欧几里德判断所有数的公约数是否为1,是就有限,不是即无限。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in)); static int steamer[]; static boolean dp[] = new boolean[10100]; static int cannot = 0;
public static void main(String[] args) { int n = scanner.nextInt(); steamer = new int[n]; for (int i = 0; i < n; i++) { steamer[i] = scanner.nextInt(); } int m = steamer[0]; for (int i = 0; i < n; i++) { m = gcd(m, steamer[i]); } if (m != 1) { System.out.println("INF"); } else { dp[0] = true; for (int i = 0; i < n; i++) { for (int j = 0; j < 10000; j++) { if (dp[j]) { dp[j + steamer[i]] = true; } } } for (int i = 0; i < 10000; i++) { if (dp[i] != true) { System.out.print(i + " "); cannot++; } } System.out.println(); System.out.println(cannot); } }
static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } }
|
分巧克力
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。
切出的巧克力需要满足:
- 形状是正方形,边长是整数
- 大小相同
例如:一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入:
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。
输出:
输出切出的正方形巧克力最大可能的边长。
样例输入:
2 10
6 5
5 6
样例输出:
2
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
二分1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in)); static int n = scanner.nextInt(); static int k = scanner.nextInt(); static int[] h = new int[n]; static int[] w = new int[n];
public static void main(String[] args) { for (int i = 0; i < n; i++) { h[i] = scanner.nextInt(); w[i] = scanner.nextInt(); }
int front = 1; int reer = 100000; while (front < reer) { int midddle = (front + reer) / 2; if (cut(midddle) >= k) { front = midddle + 1; } else { reer = midddle - 1; } }
System.out.println(front); }
static int cut(int length) { int sum = 0; for (int i = 0; i < n; i++) { sum += (h[i] / length) * (w[i] / length); } return sum; } }
|
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
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
分析:
判断某些区间的和时会重复计算,比如区间 [1,4],其和为 1+2+3+4,而 1+2+3 在区间 [1,3] 时就已经计算过了,为了减少重复,使用前缀和:
- 用 sum[i] 表示 A1 + A2 + … +Ai
- 对任意一段区间 [l, r],其和为 sum[r] - sum[l-1]
- 保证这个区间和为K倍数:(sum[r] - sum[l-1]) % k == 0
- 变形后:sum[r] % k == sum[l-1] % k
样例说明:
1 2 3 4
| 区间: 1 2 3 4 5 前缀和:1 3 6 10 15 取余: 1 1 0 0 1,这里其实还隐藏了第一个 0 补全:0 1 1 0 0 1
|
根据变形 sum[r] % k == sum[l-1] % k 可以判断出:
0 == 0:[1, 3]、[1, 4]、[4]
1 == 1:[2, 5]、[3, 5]、[2]
这里就相当于将余数相同的进行了组合:
1 2 3
| 补全: 0 1 1 0 0 1 0下标:1 2 3 (为了方便说明) 1下标: 1 2 3
|
根据变形,1号0可以和2号0,也可以和3号0组合;2号0可以和3号0组合,即组合公式C(3, 2) = 3种。
同理,1号1可以和2号1,也可以和3号1组合;2号1可以和3号1组合,组合公式同上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in)); static int n = scanner.nextInt(); static int k = scanner.nextInt(); static int[] array = new int[n + 1]; static int[] sum = new int[n + 1]; static int[] mod = new int[k]; static long count = 0;
public static void main(String[] args) { for (int i = 1; i <= n; i++) { array[i] = scanner.nextInt(); sum[i] = sum[i - 1] + array[i]; mod[sum[i] % k]++; } mod[0] = 1; for (int i = 0; i < k; i++) { count += (mod[i] * (mod[i] - 1)) / 2; } System.out.println(count); } }
|
正则问题
考虑一种简单的正则表达式:
只由 x ( ) | 组成的正则表达式。
小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是6。
输入
一个由x()|组成的正则表达式。输入长度不超过100,保证合法。
输出
这个正则表达式能接受的最长字符串的长度。
样例输入:
((xx|xxx)x|(x|xx))xx
输出:
6
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
深度搜索1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import java.io.BufferedInputStream; import java.util.Scanner;
public class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in)); static String string = scanner.next(); static int index = 0; static int length = string.length();
public static void main(String[] args) { System.out.println(dfs()); }
static long dfs() { long x = 0, total = 0; while (index < length) { char symbol = string.charAt(index); if (symbol == '(') { index++; x += dfs(); } else if (symbol == ')') { index++; break; } else if (symbol == '|') { index++; total = Math.max(x, total); x = 0; } else { index++; x++; } } return Math.max(x, total); } }
|
油漆面积
X星球的一批考古机器人正在一片废墟上考古,该区域的地面坚硬如石、平整如镜。
管理人员为方便,建立了标准的直角坐标系。
每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同,经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。
矩形的表示格式为(x1,y1,x2,y2),代表矩形的两个对角点坐标。
为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆,小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。
其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。
注意,各个矩形间可能重叠。
本题的输入为若干矩形,要求输出其覆盖的总面积。
输入格式:
第一行,一个整数n,表示有多少个矩形(1<=n<10000)
接下来的n行,每行有4个整数x1 y1 x2 y2,空格分开,表示矩形的两个对角顶点坐标。(0<= x1,y1,x2,y2 <=10000)
输出格式:
一行一个整数,表示矩形覆盖的总面积。
样例1:
3
1 5 10 10
3 1 20 20
2 7 15 17
输出:
340
样例2:
3
5 2 10 6
2 7 12 10
8 1 15 15
输出:
128
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
分析:
线段树 + 扫描线解决的典型题目,参考博客。
因为4个坐标都是整型,也可以直接使用二维数组,记录访问过的点,最后再遍历该数组。
青蛙跳杯子
X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。
*WWWBBB
其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。
X星的青蛙很有些癖好,它们只做3个动作之一:
- 跳到相邻的空杯子里。
- 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
- 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。
对于上图的局面,只要1步,就可跳成下图局面:
WWW*BBB
本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。
输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。
样例1:
*WWBB
WWBB*
输出:
2
样例2:
WWW*BBB
BBB*WWW
输出:
10
我们约定,输入的串的长度不超过15
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
广度搜索1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| import java.io.BufferedInputStream; import java.util.ArrayDeque; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Scanner;
class Main { static Scanner scanner = new Scanner(new BufferedInputStream(System.in)); static Map<String, Boolean> isVisit = new HashMap<>();
public static void main(String[] args) { String start = scanner.next(); String end = scanner.next(); bfs(start, end); }
static void bfs(String start, String end) { isVisit.put(start, true); Queue<Node> queue = new ArrayDeque<>(); queue.offer(new Node(start, 0)); while (!queue.isEmpty()) { Node headNode = queue.poll(); headNode.buildChildNodes(); for (Node node : headNode.childNode) { System.out.println(node.value + " " + node.step); if (node.value.equals(end)) { System.out.println(node.step); return; } else { queue.offer(node); } } } }
static class Node { List<Node> childNode; String value; int step;
Node(String value, int step) { this.value = value; this.step = step; }
void buildChildNodes() { this.childNode = new LinkedList<>(); int xIndex = this.value.indexOf("*"); if (xIndex == 0 && this.value.length() == 1) { return; } List<String> array = new LinkedList<>(); if (xIndex >= 1) array.add(swap(this.value, xIndex, xIndex - 1)); if (xIndex >= 2) array.add(swap(this.value, xIndex, xIndex - 2)); if (xIndex >= 3) array.add(swap(this.value, xIndex, xIndex - 3));
if (this.value.length() > xIndex + 1) array.add(swap(this.value, xIndex, xIndex + 1)); if (this.value.length() > xIndex + 2) array.add(swap(this.value, xIndex, xIndex + 2)); if (this.value.length() > xIndex + 3) array.add(swap(this.value, xIndex, xIndex + 3));
for (int i = 0; i < array.size(); i++) { if (isVisit.get(array.get(i)) == null) { this.childNode.add(new Node(array.get(i), this.step + 1)); isVisit.put(array.get(i), true); } } }
static String swap(String string, int i, int j) { char[] cs = string.toCharArray(); char tmp = cs[i]; cs[i] = cs[j]; cs[j] = tmp; return new String(cs); } } }
|