2018年第九届蓝桥杯[Java]
乘积最大
【题目描述】
给定N个整数A1, A2, … AN。请你从中选出K个数,使其乘积最大。
请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以1000000009的余数。
注意,如果X<0, 我们定义X除以1000000009的余数是负(-X)除以1000000009的余数。
即:0-((0-x) % 1000000009)
【输入】
第一行包含两个整数N和K。
以下N行每行一个整数Ai。
【输出】
一个整数,表示答案。
【样例输入】
5 3
-100000
-10000
2
100000
10000
【样例输出】
999100009
代码
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
private static long mod = 1000000009;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int k = sc.nextInt();
Long[] A = new Long[N];
for (int i = 0; i < N; i++) {
A[i] = sc.nextLong();
}
BigInteger sum = new BigInteger("1");
Arrays.sort(A);
if (A[N - 1] < 0) {
for (int i = 0, j = N - 1; i < k; i++, j--) {
sum = sum.multiply(BigInteger.valueOf(A[j]));
}
} else {
int i = 0, j = N - 1;
int flag = 0;
for (int t = 0; i <= j && t < k; t++) {
if (Math.abs(A[i]) > Math.abs(A[j])) {
sum = sum.multiply(BigInteger.valueOf(A[i]));
i++;
flag++;
} else {
sum = sum.multiply(BigInteger.valueOf(A[j]));
j--;
}
//System.out.print(sum + " ");
if(t != k - 1) {
if(sum.compareTo(BigInteger.ZERO) == -1) {
sum = BigInteger.ZERO.subtract(BigInteger.ZERO.subtract(sum).mod(BigInteger.valueOf(mod)));
}else {
sum = sum.mod(BigInteger.valueOf(mod));
}
}
//System.out.println(sum);
}
if (flag % 2 == 1) {
BigInteger t1 = new BigInteger("1");
BigInteger t2 = new BigInteger("1");
if (j == N - 1) {
t1 = t1.multiply(BigInteger.valueOf(A[i])).multiply(BigInteger.valueOf(A[i - 1]));
t2 = t2.multiply(BigInteger.valueOf(A[j])).multiply(BigInteger.ONE);
if (t1.compareTo(t2) == 1) {
sum = sum.divide(BigInteger.ONE).multiply(BigInteger.valueOf(A[i]));
} else {
sum = sum.divide(BigInteger.valueOf(A[i - 1])).multiply(BigInteger.valueOf(A[j]));
}
} else if (i == 0) {
t1 = t1.multiply(BigInteger.valueOf(A[i])).multiply(BigInteger.ONE);
t2 = t2.multiply(BigInteger.valueOf(A[j])).multiply(BigInteger.valueOf(A[j + 1]));
if (t1.compareTo(t2) == 1) {
sum = sum.divide(BigInteger.valueOf(A[j + 1])).multiply(BigInteger.valueOf(A[i]));
} else {
sum = sum.divide(BigInteger.ONE).multiply(BigInteger.valueOf(A[j]));
}
} else {
t1 = t1.multiply(BigInteger.valueOf(A[i])).multiply(BigInteger.valueOf(A[i - 1]));
t2 = t2.multiply(BigInteger.valueOf(A[j])).multiply(BigInteger.valueOf(A[j + 1]));
if (t1.compareTo(t2) == 1) {
sum = sum.divide(BigInteger.valueOf(A[j + 1])).multiply(BigInteger.valueOf(A[i]));
} else {
sum = sum.divide(BigInteger.valueOf(A[i - 1])).multiply(BigInteger.valueOf(A[j]));
}
}
}
}
if (sum.compareTo(BigInteger.ZERO) == -1) {
System.out.println(BigInteger.ZERO.subtract(BigInteger.ZERO.subtract(sum).mod(BigInteger.valueOf(mod))));
} else {
System.out.println(sum.mod(BigInteger.valueOf(mod)));
}
sc.close();
}
}
次数差
【题目描述】
x星球有26只球队,分别用a~z的26个字母代表。他们总是不停地比赛。
在某一赛段,哪个球队获胜了,就记录下代表它的字母,这样就形成一个长长的串。
国王总是询问:获胜次数最多的和获胜次数最少的有多大差距?(当然,他不关心那些一次也没获胜的,认为他们在怠工罢了)
【输入】
一个串,表示球队获胜情况(保证串的长度<1000)
【输出】
一个数字,表示出现次数最多的字母比出现次数最少的字母多了多少次。
【样例输入】
abaabcaa
【样例输出】
4
代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.next();
sc.close();
int[] x = new int[26];
for(int i = 0; i < str.length(); i++) {
int t = str.charAt(i) - 'a';
x[t]++;
}
int max = 0;
int min = 1000;
for(int i = 0; i < x.length; i++) {
if(x[i] > max) {
max = x[i];
}
if(x[i] < min && x[i] != 0) {
min = x[i];
}
}
System.out.println(max - min);
}
}
小朋友崇拜圈
【题目描述】
班里N个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己)。
在一个游戏中,需要小朋友坐一个圈,
每个小朋友都有自己最崇拜的小朋友在他的右手边。
求满足条件的圈最大多少人?
小朋友编号为1,2,3,…N
【输入】
输入第一行,一个整数N(3<N<100000)
接下来一行N个整数,由空格分开。
【输出】
要求输出一个整数,表示满足条件的最大圈的人数。
【样例输入】
9
3 4 2 5 3 8 4 6 9
【样例输出】
4
代码
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
private static int[] a; //保存数据
private static int[] d; //存储入度
private static int[] del; //标志小朋友是否可能在圈子中
private static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
a = new int[n + 1];
d = new int[n + 1];
del = new int[n + 1];
for(int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
d[a[i]]++; //1号崇拜三号,则被崇拜的3号入度+1
}
f(); //剔除掉入队为0的小朋友,他们不可能在任何一个圈中
int max = 0;
//剔除掉不在圈中的人后,剩下的小朋友一定在某个圈中
for(int i = 1; i <= n; i++) {
if(del[i] == 1) {//被剔除的人
continue;
}
del[i] = 1; //从这个人开始去找圈,这也是要剔除的,防止重复找
int pos = i, sum = 1;
while(a[pos] != i) {
sum++;
del[pos] = 1;
pos = a[pos];
}
max = Math.max(max, sum);
}
System.out.println(max);
sc.close();
}
private static void f() {
// TODO Auto-generated method stub
Queue<Integer> queue = new LinkedList<>();
for(int i = 1; i <= n; i++) {
//入度为0的小朋友,表示没有被人崇拜
if(d[i] == 0) {
del[i] = 1; //剔除
d[a[i]]--; //其崇拜的小朋友入度-1
queue.offer(i); //入度为0入队
}
}
while(!queue.isEmpty()) {
int t = queue.poll(); //出队并删除
int t2 = a[t]; //t2表示被编号t崇拜的人
//如果当编号为t的人崇拜的编号为t2的人入度为0,则表示只有t崇拜t2,则t2也不可能在任何一个圈中
if(d[t2] == 0) {
del[t2] = 1; //剔除
queue.offer(t2); //入栈
if(d[a[t2]] > 0) {
d[a[t2]]--;
}
}
}
}
}
耐摔指数
【题目描述】
x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。
x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。
如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n
为了减少测试次数,从每个厂家抽样3部手机参加测试。
如果已知了测试塔的高度,并且采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
【输入】
一个整数n(3<n<10000),表示测试塔的高度。
【输出】
输出一个整数,表示最多测试多少次。
【样例输入】
3
【样例输出】
2
代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] dp = new int[3 + 1][n + 1];
for(int i = 1; i <= n; i++) {
dp[1][i] = i;
}
for(int i = 2; i <= 3; i++) {
for(int j = 1; j <= n; j++) {
dp[i][j] = dp[i][j - 1] + 1;
for(int k = 2; k <= j; k++) {
dp[i][j] = Math.min(dp[i][j], Math.max(dp[i - 1][k - 1], dp[i][j - k]) + 1);
}
}
}
System.out.println(dp[3][n]);
sc.close();
}
}
缩位求和
【题目描述】
在电子计算机普及以前,人们经常用一个粗略的方法来验算四则运算是否正确。
比如:248 * 15 = 3720
把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是1位数,得
2 + 4 + 8 = 14 ==> 1 + 4 = 5;
1 + 5 = 6
5 * 6
而结果逐位求和为 3
5 * 6 的结果逐位求和与3符合,说明正确的可能性很大!!(不能排除错误)
请你写一个计算机程序,对给定的字符串逐位求和:
【输入】
输入为一个由数字组成的串,表示n位数(n<1000);
【输出】
输出为一位数,表示反复逐位求和的结果。
【样例输入】
35379
【样例输出】
9
代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.next();
while(str.length() != 1) {
int n = 0;
for(int i = 0; i < str.length(); i++) {
n += str.charAt(i) - '0';
}
str = String.valueOf(n);
//System.out.println(n);
}
System.out.println(str);
sc.close();
}
}
约瑟夫环
【题目描述】
n 个人的编号是 1~n,如果他们依编号按顺时针排成一个圆圈,从编号是1的人开始顺时针报数。
(报数是从1报起)当报到 k 的时候,这个人就退出游戏圈。下一个人重新从1开始报数。
求最后剩下的人的编号。这就是著名的约瑟夫环问题。
本题目就是已知 n,k 的情况下,求最后剩下的人的编号。
【输入】
题目的输入是一行,2个空格分开的整数n, k
【输出】
要求输出一个整数,表示最后剩下的人的编号。
【样例输入】
10 3
【样例输出】
4
代码
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int num = 0;
for(int i = 1; i <= n; i++) {
num = (num + m) % i;
}
System.out.println(num + 1);
sc.close();
}
}
调手表
【题目描述】
小明买了块高端大气上档次的电子手表,他正准备调时间呢。
在 M78 星云,时间的计量单位和地球上不同,M78 星云的一个小时有 n 分钟。
大家都知道,手表只有一个按钮可以把当前的数加一。在调分钟的时候,如果当前显示的数是 0 ,那么按一下按钮就会变成 1,再按一次变成 2 。如果当前的数是 n - 1,按一次后会变成 0 。
作为强迫症患者,小明一定要把手表的时间调对。如果手表上的时间比当前时间多1,则要按 n - 1 次加一按钮才能调回正确时间。
小明想,如果手表可以再添加一个按钮,表示把当前的数加 k 该多好啊⋯⋯
他想知道,如果有了这个 +k 按钮,按照最优策略按键,从任意一个分钟数调到另外任意一个分钟数最多要按多少次。
注意,按 +k 按钮时,如果加k后数字超过n-1,则会对n取模。
比如,n=10, k=6 的时候,假设当前时间是0,连按2次 +k 按钮,则调为2。
【输入】
一行两个整数 n, k ,意义如题。
【输出】
一行一个整数
表示:按照最优策略按键,从一个时间调到另一个时间最多要按多少次。
【样例输入】
5 3
【样例输出】
2
代码
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
int[] x = new int[n];
Queue<Integer> queue = new LinkedList<>();
queue.add(0);
while(!queue.isEmpty()) {
int t = queue.poll();
int k1 = (t + 1) % n;
if(x[k1] == 0 && k1 != 0) {
x[k1] = x[t] + 1;
queue.add(k1);
}
int k2 = (t + k) % n;
if(x[k2] == 0 && k2 != 0) {
x[k2] = x[t] + 1;
queue.add(k2);
}
}
int max = 0;
for(int i = 0; i < n; i++) {
if(max < x[i]) {
max = x[i];
}
//System.out.print(x[i] + " ");
}
System.out.println(max);
sc.close();
}
}
交换次数
【题目描述】
IT产业人才需求节节攀升。业内巨头百度、阿里巴巴、腾讯(简称BAT)在某海滩进行招聘活动。
招聘部门一字排开。由于是自由抢占席位,三大公司的席位随机交错在一起,形如:
ABABTATT,这使得应聘者十分别扭。
于是,管理部门要求招聘方进行必要的交换位置,使得每个集团的席位都挨在一起。即最后形如:
BBAAATTT 这样的形状,当然,也可能是:
AAABBTTT 等。
现在,假设每次只能交换2个席位,并且知道现在的席位分布,
你的任务是计算:要使每个集团的招聘席位都挨在一起需要至少进行多少次交换动作。
【输入】
输入是一行n个字符(只含有字母B、A或T),表示现在的席位分布。
【输出】
输出是一个整数,表示至少交换次数。
【样例输入】
TABTABBTTTT
【样例输出】
3
代码
import java.util.HashMap;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String string = scanner.next();
HashMap<Character, Integer> stringmap = new HashMap<Character, Integer>();
/*
* 测试用例长度
* while (string.length() < 100000) {
string += string;
}
*/
// 统计ABT三个字母的次数
for (int i = 0; i < string.length(); i++) {
char charAt = string.charAt(i);
if (stringmap.containsKey(charAt)) {
stringmap.put(charAt, stringmap.get(charAt) + 1);
} else {
stringmap.put(charAt, 1);
}
}
char[] carr = { 'A', 'B', 'T' };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int j2 = 0; j2 < 3; j2++) {
if (i != j && i != j2 && j != j2) {
// ABT全排列有6种方式
// s1表示第一个字母长度的字符串,s2表示第二个字母长度的字符串,s3表示第三个字母长度的字符串
// c1、c2、c3表示字母的排列方式,如排列方式为BAT的话,则c1 = 'B', c2 = 'A', c3 = 'T'
String s1 = string.substring(0, stringmap.get(carr[i]));
String s2 = string.substring(s1.length(), s1.length() + stringmap.get(carr[j]));
cal(s1, s2, carr[i], carr[j], carr[j2]);
}
}
}
}
System.out.println(result);
}
static int result = Integer.MAX_VALUE;
private static void cal(String s1, String s2, char c1, char c2, char c3) {
// 统计s1中字母的个数
HashMap<Character, Integer> map1 = new HashMap<Character, Integer>();
map1.put(c1, 0);
map1.put(c2, 0);
map1.put(c3, 0);
for (int i = 0; i < s1.length(); i++) {
char charAt = s1.charAt(i);
map1.put(charAt, map1.get(charAt) + 1);
}
// 统计s2中字母的个数
HashMap<Character, Integer> map2 = new HashMap<Character, Integer>();
map2.put(c1, 0);
map2.put(c2, 0);
map2.put(c3, 0);
for (int i = 0; i < s2.length(); i++) {
char charAt = s2.charAt(i);
map2.put(charAt, map2.get(charAt) + 1);
}
// 总需交换次数为s1.length()-s1(c1)+s2.length()-s2(c2)-Math.min(s2(c1),s1(c2))
// 第一字母交换次数+第二字母交换次数-第二字母刚好在第一字母长度内的个数与第一字母刚好在第二字母长度内的个数的最小值
int sum = s1.length() - map1.get(c1) + s2.length() - map2.get(c2) - Math.min(map1.get(c2), map2.get(c1));
result = Math.min(sum, result);
}
}
整理玩具
【题目描述】
小明有一套玩具,一共包含NxM个部件。这些部件摆放在一个包含NxM个小格子的玩具盒中,每个小格子中恰好摆放一个部件。
每一个部件上标记有一个0~9的整数,有可能有多个部件标记相同的整数。
小明对玩具的摆放有特殊的要求:标记相同整数的部件必须摆在一起,组成一个矩形形状。
如以下摆放是满足要求的:
00022
00033
44444
12244
12244
12233
01234
56789
以下摆放不满足要求:
11122
11122
33311
111111
122221
122221
111111
11122
11113
33333
给出一种摆放方式,请你判断是否符合小明的要求。
【输入】
输入包含多组数据。
第一行包含一个整数T,代表数据组数。 (1 <= T <= 10)
以下包含T组数据。
每组数据第一行包含两个整数N和M。 (1 <= N, M <= 10)
以下包含N行M列的矩阵,代表摆放方式。
【输出】
对于每组数据,输出YES或者NO代表是否符合小明的要求。
【样例输入】
3
3 5
00022
00033
44444
3 5
11122
11122
33311
2 5
01234
56789
【样例输出】
YES
NO
YES
代码
import java.util.Scanner;
public class Main {
public static int[][] t;
public static int[][] dir;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
for(int i = 0; i < T; i++) {
int N = sc.nextInt();
int M = sc.nextInt();
t = new int[N][M];
for(int j = 0; j < N; j++) {
char[] s = sc.next().toCharArray();
for(int k = 0; k < M; k++) {
t[j][k] = s[k] - '0';
}
}
boolean flag = f(N, M);
if(flag) {
System.out.println("YES");
}else {
System.out.println("NO");
}
}
sc.close();
}
private static boolean f(int n, int m) {
dir = new int[n][m];
int[] x = new int[10];
boolean flag = true;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(x[t[i][j]] != 0 && dir[i][j] == 0) {
return flag = false;
}
if(dir[i][j] == 0) {
x[t[i][j]] = 1;
if(!check(t[i][j], n, m, i, j)) {
return false;
}
}
}
}
return flag;
}
private static boolean check(int x, int n, int m, int i, int j) {
int a = 0; int b = 0;
for(int k = j; k < m; k++) {
if(t[i][k] == x) {
a++;
}
}
for(int k = i; k < n; k++) {
if(t[k][j] == x) {
b++;
}
}
for(int k = i; k < i + b; k++) {
for(int l = j; l < j + a; l++) {
dir[k][l] = 1;
if(t[k][l] != x) {
return false;
}
}
}
return true;
}
}