文章目录
1.标题:猜年龄
小明带两个妹妹参加元宵灯会。别人问她们多大了,她们调皮地说:“我们俩的年龄之积是年龄之和的6倍”。小明又补充说:“她们可不是双胞胎,年龄差肯定也不超过8岁啊。”
请你写出:小明的较小的妹妹的年龄。
注意: 只写一个人的年龄数字,请通过浏览器提交答案。不要书写任何多余的内容。
两层for + 一个if
package A14;
public class Demo01 {
public static void main(String[] args) {
for (int i = 1; i < 20; i++) {
for (int j = 1; j < 20; j++) {
if (i*j == (i+j)*6 && i > j && i - j <= 8) System.out.println(i + " " + j);
}
}
}
}
2.标题:李白打酒
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。
注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。
深搜 - 消耗型题
package A14;
public class Demo02 {
// 酒壶中有酒2斗,逢店加一倍,遇花喝一斗。
// 一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了
static int ans = 0;
public static void f(int dian, int hua, int jiu){
if (dian == 0 && hua==0 && jiu==1) ans++; // 花和店都遇到完了,并且此时还有一斗酒
if (dian > 0) f(dian - 1, hua, jiu * 2); // 碰到店
if (hua > 0) f(dian, hua - 1, jiu - 1); // 看到花
}
public static void main(String[] args) {
f(5, 9, 2);
System.out.println(ans);
}
}
3.标题:神奇算式
由4个不同的数字,组成的一个乘法算式,它们的乘积仍然由这4个数字组成。
比如:
210 x 6 = 1260
8 x 473 = 3784
27 x 81 = 2187
都符合要求。
如果满足乘法交换律的算式算作同一种情况,那么,包含上边已列出的3种情况,一共有多少种满足要求的算式。
请填写该数字,通过浏览器提交答案,不要填写多余内容(例如:列出所有算式)。
解法一
乘法满足交换率,所以最终结果要除2
package A14;
public class Demo03 {
static int ans = 0;
static int[] arr;
public static void main(String[] args) {
int a,b,c,d;
for (int i = 1000; i <= 9999; i++) {
a = i/1000;
b = i/100%10;
c = i/10%10;
d = i%10;
if (a!=b&&a!=c&&a!=d&&b!=c&&b!=d&&c!=d){
arr = new int[]{
a,b,c,d};
f(i, 0);
}
}
System.out.println(ans / 2);
}
// 对数组中的4位数进行全排列
private static void f(int x, int step) {
if (step == arr.length - 1){
check(x);
return;
}
int t;
for (int j = step; j < arr.length; j++) {
t = arr[step]; // 确保4位数每一位都在各个位出现过
arr[step] = arr[j];
arr[j] = t;
f(x, step+1);
t = arr[step]; // 回溯, 清除状态
arr[step] = arr[j];
arr[j] = t;
}
}
public static void check(int x){
// i控制乘号的位置
for (int i = 0; i <= 2; i++) {
int a = 0, b = 0;
for (int j = 0; j <= i; j++) {
a = a * 10 + arr[j];
}
for (int j = i+1; j <= 3; j++) {
b = b*10 + arr[j];
}
if (a * b == x){
ans++;
System.out.printf("%s * %s = %s\n", a, b, a * b);
}
}
}
}
解法二
package A14;
import java.util.Arrays;
public class Demo03_2 {
static int ans;
public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (i!=j){
for (int k = 0; k < 10; k++) {
if (k!=i&&k!=j){
for (int l = 0; l < 10; l++) {
if (l!=i&&l!=j&&l!=k){
String src = "" + i + j + k + l;
String r1 = i * (j * 100 + k * 10 + l) + "";
if (check(src, r1)) ans++;
String r2 = (i*10 + j) * (k * 10 + l) + "";
if (check(src, r2)) ans++;
String r3 = (i*100 + j * 10 + k) * (l) + "";
if (check(src, r3)) ans++;
}
}
}
}
}
}
}
System.out.println(ans / 2);
}
private static boolean check(String s1, String s2) {
char[] chars1 = s1.toCharArray();
char[] chars2 = s2.toCharArray();
Arrays.sort(chars1);
Arrays.sort(chars2);
return new String(chars1).equals(new String(chars2));
}
}
4.标题:写日志
写日志是程序的常见任务。现在要求在 t1.log, t2.log, t3.log 三个文件间轮流写入日志。也就是说第一次写入t1.log,第二次写入t2.log,… 第四次仍然写入t1.log,如此反复。
下面的代码模拟了这种轮流写入不同日志文件的逻辑。
public class A
{
private static int n = 1;
public static void write(String msg)
{
String filename = "t" + n + ".log";
n = ____________;
System.out.println("write to file: " + filename + " " + msg);
}
}
请填写划线部分缺失的代码。通过浏览器提交答案。
注意:不要填写题面已有的内容,也不要填写任何说明、解释文字。
答案1: (n + 1) % 3 == 0 ? 3: (n+1)%3
答案2: n % 3 + 1
5.标题:锦标赛
如果要在n个数据中挑选出第一大和第二大的数据(要求输出数据所在位置和值),使用什么方法比较的次数最少?我们可以从体育锦标赛中受到启发。
如图【1.png】所示,8个选手的锦标赛,先两两捉对比拼,淘汰一半。优胜者再两两比拼…直到决出第一名。
第一名输出后,只要对黄色标示的位置重新比赛即可。
下面的代码实现了这个算法(假设数据中没有相同值)。
代码中需要用一个数组来表示图中的树(注意,这是个满二叉树, 不足需要补齐)。它不是存储数据本身,而是存储了数据的下标。
第一个数据输出后,它所在的位置被标识为-1
class A{
//a 表示待处理的数据,长度如果不是2的次幂,则不足位置补为-1
static void pick(int[] a)
{
int n = 1;
while(n<a.length) n *= 2;
int[] b = new int[2*n-1];
for(int i=0; i<n; i++){
if(i<a.length)
b[n-1+i] = i;
else
b[n-1+i] = -1;
}
//从最后一个向前处理
for(int i=b.length-1; i>0; i-=2){
if(b[i]<0){
if(b[i-1]>=0)
b[(i-1)/2] = b[i-1];
else
b[(i-1)/2] = -1;
}
else{
if(a[b[i]]>a[b[i-1]])
b[(i-1)/2] = b[i];
else
b[(i-1)/2] = b[i-1];
}
}
//输出树根
System.out.println(b[0] + ": " + a[b[0]]);
//值等于根元素的位置需要重新pk
pk(a,b,0,b[0]);
//再次输出树根
System.out.println(b[0] + ": " + a[b[0]]);
}
// a 表示待处理数据,b 二叉树,k 当前要重新比拼的位置,v 已经决胜出的值
static void pk(int[] a, int[] b, int k, int v)
{
int k1 = k*2+1;
int k2 = k1 + 1;
if(k1>=b.length || k2>=b.length){
b[k] = -1;
return;
}
if(b[k1]==v)
pk(a,b,k1,v);
else
pk(a,b,k2,v);
//重新比较
if(b[k1]<0){
if(b[k2]>=0)
b[k] = b[k2];
else
b[k] = -1;
return;
}
if(b[k2]<0){
if(b[k1]>=0)
b[k] = b[k1];
else
b[k] = -1;
return;
}
if(__________________________) //填空
b[k] = b[k1];
else
b[k] = b[k2];
}
}
请仔细分析流程,填写缺失的代码。
通过浏览器提交答案,只填写缺失的代码,不要填写已有代码或其它说明语句等。
注意pk函数里面的这段代码的功能是先沿着着图中黄色标记从根结点到叶子节点,然后将叶子节点中存储最大值索引的位置标记为-1。
注意: b中存储的是索引,而不是值,所以在填空处应该比较左子树与右子树存储的a数组的索引对应的值的大小,然后将值的大的索引赋给父节点。
答案是: a[b[k1]] > a[b[k2]]
6.标题:六角填数
如图【1.png】所示六角形中,填入1~12的数字。
使得每条直线上的数字之和都相同。
图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?
请通过浏览器提交答案,不要填写多余的内容。
用数组存储剩下可填的9个数{2, 4, 5, 6, 7, 9, 10, 11, 12},将空处用索引0-8标记,对数组进行全排列,然后按索引填到空处,验证6条斜线的数加起来是否相等,最后输出arr[3]处的数。
package A14;
public class Demo06 {
static int[] arr = {
2, 4, 5, 6, 7, 9, 10, 11, 12};
public static void main(String[] args) {
f(0);
}
// 全排列
public static void f(int step){
if (step >= arr.length){
check();
return;
}
int t;
for (int i = step; i < arr.length; i++) {
t = arr[step];
arr[step] = arr[i];
arr[i] = t;
f(step+1);
t = arr[step];
arr[step] = arr[i];
arr[i] = t;
}
}
public static void check(){
int r1 = 1 + arr[0] + arr[3] + arr[5];
int r2 = 1 + arr[1] + arr[4] + arr[8];
int r3 = arr[5] + arr[6] + arr[7] + arr[8];
int r4 = 8 + arr[0] + arr[1] + arr[2];
int r5 = 11 + arr[3] + arr[6];
int r6 = 3 + arr[2] + arr[4] + arr[7];
if (r1 == r2 && r2 == r3 && r3 == r4 && r4 == r5 && r5 == r6){
System.out.println("* = " + arr[3]);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
}
7.标题:绳圈
今有 100 根绳子,当然会有 200 个绳头。
如果任意取绳头两两配对,把所有绳头都打结连接起来。最后会形成若干个绳圈(不考虑是否套在一起)。
我们的问题是:请计算最后将形成多少个绳圈的概率最大?
注意:结果是一个整数,请通过浏览器提交该数字。不要填写多余的内容。
假设n-1根绳子配对完毕共有c(n - 1)种情况,那么在此基础上加一根绳子,重新进行配对,有以下两种情况可以选择:
(1)绳圈个数不变,在c(n - 1)中情况的每个情况下,将每种情况下的n - 1根已配对完毕的绳头中选择一个绳头(断开),将当前新添加的一根绳子绳头打结连接起来,由于新添加的绳子有两个绳头,可交换,所以要乘以2。 = > c(n - 1) * 2 * (n - 1)
(2)增加一个绳圈,直接让新添加的绳子两个绳头直接相连。=> c(n - 1) * 1
所以c(n) = c(n - 1) * 2 * ( n - 1) + c(n - 1) = c(n - 1) * (2 * n - 1)
于是有 f[i][i] = 1/c[i] --> f[i][i] 表示i根绳i个圈,只有 1 种配对方法,而全部配对方法为c[i]
f[i][1] = f[i-1][1] * c[i-1] * (i-1) * 2 / c[i] --> i根绳1个圈的概率 = i - 1根绳子围成1个圈的概率 * 总数(即配对数) ,有(i-1) 个地方可以断开接入得出配对数,然后除以总数得概率
进一步f[i][j] = (f[i-1][j] * c[i-1] * (i-1) * 2 + f[i-1][j-1] * c[i-1]) / c[i],
fij=
情况1:维持j个圈,有-1个地方可以断开将新绳子接入f[i-1][j] * c[i-1] * (i-1) * 2
情况2: i - 1根绳子组成j-1个圈,新绳子自成一圈
两种情况相加 / 总数 得出概率
再根据c[i]和c[i-1]的递推关系将c[i]和c[i-1]约去得到: f[i][j] = (f[i-1][j] * (i-1) * 2 + f[i-1][j-1]) / (2i-1)
package A14;
public class Demo07 {
public static void main(String[] args) {
double[][] f = new double[101][101];
f[1][1] = 1;
for (int sheng = 2; sheng <= 100; sheng++) {
f[sheng][1] = f[sheng - 1][1] * (sheng - 1) * 2 / (2 * sheng - 1);
for (int quan = 2; quan <= sheng; quan++) {
f[sheng][quan] = (f[sheng - 1][quan - 1] + f[sheng - 1][quan] * (sheng - 1) * 2) / (2 * sheng - 1);
}
}
double p = -1;
int max = -1;
for (int quan = 1; quan <= 100; quan++) {
if (p < f[100][quan]){
p = f[100][quan];
max = quan;
}
}
System.out.println(max);
}
}
8.标题:兰顿蚂蚁
兰顿蚂蚁,是于1986年,由克里斯·兰顿提出来的,属于细胞自动机的一种。
平面上的正方形格子被填上黑色或白色。在其中一格正方形内有一只“蚂蚁”。
蚂蚁的头部朝向为:上下左右其中一方。
蚂蚁的移动规则十分简单:
若蚂蚁在黑格,右转90度,将该格改为白格,并向前移一格;
若蚂蚁在白格,左转90度,将该格改为黑格,并向前移一格。
规则虽然简单,蚂蚁的行为却十分复杂。刚刚开始时留下的路线都会有接近对称,像是会重复,但不论起始状态如何,蚂蚁经过漫长的混乱活动后,会开辟出一条规则的“高速公路”。
蚂蚁的路线是很难事先预测的。
你的任务是根据初始状态,用计算机模拟兰顿蚂蚁在第n步行走后所处的位置。
【数据格式】
输入数据的第一行是 m n 两个整数(3 < m, n < 100),表示正方形格子的行数和列数。
接下来是 m 行数据。
每行数据为 n 个被空格分开的数字。0 表示白格,1 表示黑格。
接下来是一行数据:x y s k, 其中x y为整数,表示蚂蚁所在行号和列号(行号从上到下增长,列号从左到右增长,都是从0开始编号)。s 是一个大写字母,表示蚂蚁头的朝向,我们约定:上下左右分别用:UDLR表示。k 表示蚂蚁走的步数。
输出数据为两个空格分开的整数 p q, 分别表示蚂蚁在k步后,所处格子的行号和列号。
例如, 输入:
5 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
2 3 L 5
程序应该输出:
1 3
再例如, 输入:
3 3
0 0 0
1 1 1
1 1 1
1 1 U 6
程序应该输出:
0 0
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
解法一
package A14;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Demo08 {
static Map<String, String> black = new HashMap<>();
static Map<String, String> white = new HashMap<>();
static int[][] arr;
static String s;
static int x;
static int y;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int m = scan.nextInt();
int n = scan.nextInt();
arr = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
arr[i][j] = scan.nextInt();
}
}
x = scan.nextInt();
y = scan.nextInt();
s = scan.next();
int k = scan.nextInt();
black.put("U", "R");
black.put("R", "D");
black.put("D", "L");
black.put("L", "U");
white.put("U", "L");
white.put("L", "D");
white.put("D", "R");
white.put("R", "U");
for (int i = 0; i < k; i++) {
move();
}
System.out.println(x + " " + y);
}
public static void move(){
if (arr[x][y] == 1){
arr[x][y] = 0;
s = black.get(s);
}else{
arr[x][y] = 1;
s = white.get(s);
}
if (s.equals("U")){
x -= 1;
}else if (s.equals("D")){
x += 1;
}else if (s.equals("L")){
y -= 1;
}else {
y += 1;
}
}
}
解法二
package A14;
import java.util.Scanner;
public class Demo08_2 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int m = scan.nextInt();
int n = scan.nextInt();
int[][] arr = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
arr[i][j] = scan.nextInt();
}
}
int x = scan.nextInt();
int y = scan.nextInt();
String s = scan.next();
int k = scan.nextInt();
int d = getD(s);
for (int i = 0; i < k; i++) {
if (arr[x][y] == 1){
arr[x][y] = 0;
d = d % 4 + 1;
}else{
arr[x][y] = 1;
d--;
if (d == 0) d = 4;
}
if (d == 1){
x--;
}else if (d == 2){
y++;
}else if (d == 3){
x++;
}else {
y--;
}
}
System.out.println(x + " " + y);
}
private static int getD(String s) {
if (s.equals("U")){
return 1;
}else if (s.equals("R")){
return 2;
}else if (s.equals("D")){
return 3;
}else {
return 4;
}
}
}
9.标题:斐波那契
斐波那契数列大家都非常熟悉。它的定义是:
f(x) = 1 … (x=1,2)
f(x) = f(x-1) + f(x-2) … (x>2)
对于给定的整数 n 和 m,我们希望求出:
f(1) + f(2) + … + f(n) 的值。但这个值可能非常大,所以我们把它对 f(m) 取模。
公式参见【图1.png】
但这个数字依然很大,所以需要再对 p 求模。
【数据格式】
输入为一行用空格分开的整数 n m p (0 < n, m, p < 10^18)
输出为1个整数
例如,如果输入:
2 3 5
程序应该输出:
0
再例如,输入:
15 11 29
程序应该输出:
25
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
// 1.由定义fib(n) = fib(n+2)-fib(n+1)
// 2.由1得∑f(n) = f(n+2)-1;
// 如果m >= n + 2 那么f(m) > ∑f(n),结果是(f(n + 2) - 1) % p
// 否则 结果为(f(n + 2) - 1)% f(m) % p == f(n + 2) % f(m) % p - 1
菲波那契数列的快速幂矩阵求法
10.标题:波动数列
观察这个数列:
1 3 0 2 -1 1 -2 …
这个数列中后一项总是比前一项增加2或者减少3。
栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
【数据格式】
输入的第一行包含四个整数 n s a b,含义如前面说述。
输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。
例如,输入:
4 10 2 3
程序应该输出:
2
【样例说明】
这两个数列分别是2 4 1 3和7 4 1 -2。
【数据规模与约定】
对于10%的数据,1<=n<=5,0<=s<=5,1<=a,b<=5;
对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
爆搜一波得10分
…
01 猜年龄 简单运算
02 李白打酒 递归
03 神奇算式 枚举,去重复
04 写日志 取余,简单运算
05 锦标赛 梳理代码逻辑,数组表示树,树的递归
06 六角填数 全排列
07 绳圈 dp问题
08 兰顿蚂蚁 模拟
09 斐波那契 1.掌握性质,2.斐波那契的矩阵快速幂解法,3.BigInteger
10 波动数列 高级dp,01背包