- 结果填空题
- A - 组队
- B - 不同子串
- C - 数列求值
- D - 数的分解
- E - 迷宫
- 程序设计题:
- F - 特别数的和
- G - 外卖店优先级
- H - 人物相关性分析
- I - 后缀表达式
结果填空题
A - 组队
答案解析
从一号位到五号位,每个号位选出一个人,且每个号位所选的人编号不能相同,这个是隐藏的要求。
直接打开计算器计算即可,97 + 99 + 99 + 97 + 98 = 490 选法不唯一, 和唯一。
B - 不同子串
答案解析
用双重循环从左到右对原字符串进行截取,然后存入集合里,最后集合的大小就是答案,结果是100。
import java.util.HashSet;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
HashSet<String> set = new HashSet<String>();
String str = sc.nextLine();
for (int i = 0; i < str.length(); i++) {
for (int j = i + 1; j <= str.length(); j++) {
set.add(str.substring(i, j));
}
}
System.out.println(set.size());
}
}
C - 数列求值
答案解析
题目一看好像是斐波那契数列题目,但是题目要的是第20190324项的后四位数字。数据太大了,大数类也解决不了这个问题。但只求后四位,我们可以发现,不管前面数位是怎样的,后四位数字只与两个加数的后四位有关。答案是4659
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
int a = 1, b = 1, c = 1;
int ans = 0;
for (int i = 4; i <= 20190324; i++) {
ans = (a + b + c) % 10000;
a = b;
b = c;
c = ans;
}
System.out.println(ans);
}
}
D - 数的分解
答案解析
从小到大枚举 i、j、k,并且 i < j < k,防止了重复情况的出现,并且每次判断 i、j、k是否含有 2 或者 4。答案是40785。
import java.util.Scanner;
public class Main {
public static boolean judge(int x) {
while (x > 0) {
if (x % 10 == 2 || x % 10 == 4)
return true;
x /= 10;
}
return false;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int ans = 0;
for (int i = 1; i < 2019; i++) {
if (judge(i))
continue;
for (int j = i + 1; j + i < 2019; j++) {
if (judge(j))
continue;
for (int k = j + 1; k + j < 2019; k++) {
if (judge(k))
continue;
if (i + j + k == 2019) {
ans++;
System.out.println(i + " " + j + " " + k + " " + (i + j + k));
}
}
}
}
System.out.println(ans);
}
}
E - 迷宫
答案解析
求最短路径,典型的bfs题目,唯一要注意就是,这道题是有方向优先的,也就是D、L、R、U方向。还有要注意的就是输入的是字符串,要先转换为整型二维数组。
package ten;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class B_e {
//30行50列
static int n = 30, m = 50;
static int[][] num = new int[n + 1][m + 1];
static boolean[][] vis = new boolean[n+1][m+1];
//这里要跟对应D、L、R、U的方向
static int[] dx = {1,0,0,-1};
static int[] dy = {0,-1,1,0};
static char[] ch = {'D','L','R','U'};
static void bfs() {
//标记没有走过
for (int i = 1; i <= n; i++) {
for(int j=1;j<=m;j++) {
vis[i][j] = false;
}
}
Point Point = new Point(1,1);
Point.path = "";
//用队列来进行bfs
Queue<Point> q = new LinkedList<Point>();
q.offer(Point);
vis[1][1] = true;
while(!q.isEmpty()) {
Point t = q.poll();
if (t.x == n && t.y == m) {
System.out.println(t.path);
break;
}
for (int i=0;i<4;i++) {
int xx = t.x + dx[i];
int yy = t.y + dy[i];
if (xx >=1 && xx <=n && yy>=1 && yy<=m && !vis[xx][yy] && num[xx][yy]==0) {
vis[xx][yy] = true;
Point p = new Point(xx,yy);
p.path = t.path + ch[i];
q.offer(p);
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
//要把输入的字符串转换为数字
for (int i = 1; i <= 30; i++) {
String str = sc.nextLine();
for (int j = 0; j < m; j++) {
num[i][j + 1] = str.charAt(j) - '0';
}
}
bfs();
}
}
class Point {
//记录坐标
int x,y;
//记录路径
String path;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
程序设计题:
F - 特别数的和
答案解析
求出1 ~ n中包含2, 0, 1, 9这几个的数的数字之和。数据规模是10000, 所以可以直接枚举1 ~ n中所有的数,判断其是否包含2,0,1,9其中的一个,有就加上。 时间复杂度最高是O(5∗n),完全不用担心超时。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int ans = 0;
for(int i = 1; i<=n;i++) {
int x = i;
while(x > 0) {
if(x%10==2 || x%10==1 || x%10==0 || x%10==9) {
ans += i;
break;
}
x/=10;
}
}
System.out.println(ans);
}
}
G - 外卖店优先级
答案解析
- 数据规模最大是10^5,所以该题的时间复杂度必须小于 o(n ^ 2),也就是说不能直接双重遍历,这样会超时。
- 可以先按照时间ts从小到大排序,然后遍历排序后的这m条信息中时间小于等于T的信息。
- 没接过订单的店铺肯定优先级会是 0 ,这些我们不用管他,只需要处理那些有订单的店铺。
- 有订单的店铺,我们把它们放入set集合。
- 这里有个关键的点,就是,我们要记住有订单店铺上一次有订单的时间,这样我们每次遍历就可以把这个店铺两次订单之间没有订单的时间减掉,不断地更新。
- 最后我们再遍历set集合,也就是有订单的店铺,然后要再处理一下,剪掉T时刻到上一次有订单的时刻之间没有订单的时间长度,然后判断一下是否在优先缓存当中。
import java.util.Arrays;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
class Node implements Comparable<Node> {
int ts, id;
public int compareTo(Node a) {
if (this.ts == a.ts)
return this.id - a.id; // 按照id值从小到大排序
// 若要按照id从大到小排序,就用 a.id - this.id;
return this.ts - a.ts; // 按照ts值从小到大排序
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt(), t = sc.nextInt();
Node[] node = new Node[m + 1];
for (int i = 1; i <= m; i++) {
node[i] = new Node(); // 类对象必须初始化,分配内存空间
node[i].ts = sc.nextInt();
node[i].id = sc.nextInt();
}
Arrays.sort(node, 1, m + 1);
int[] last = new int[100010];
int[] score = new int[100010];
boolean[] vis = new boolean[100010];
Set<Integer> set = new TreeSet<Integer>();
for (int i = 1; i <= m && node[i].ts <= t; i++) {
int id = node[i].id;
int ts = node[i].ts;
set.add(node[i].id);
if (ts - last[id] > 1)
score[id] -= (ts - last[id] - 1);
if (score[id] < 0)
score[id] = 0;
if (score[id] <= 3)
vis[id] = false;
score[id] += 2;
if (score[id] > 5)
vis[id] = true;
last[id] = ts;
}
int ans = 0;
for(int i:set) {
if(last[i] < t) {
score[i] -= (t-last[i]);
}
if(score[i]<=3) vis[i] = false;
if(vis[i]) ans++;
}
System.out.println(ans);
}
}
H - 人物相关性分析
答案解析
- 这个做法会超时,只能过一部分样例。
- 首先先预处理字符串,把字符串按照空格和 “ . ” 分割开来,并用一个数组记录每个单词的长度。然后暴力遍历,计算每个“Alice”在安全范围内的“Bob”数目 ,再暴力一边,计算每个“Bob”在安全范围内的“Alice”数目。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int K = sc.nextInt();
sc.nextLine(); //吸掉换行符
String str = sc.nextLine();
//用正则表达式按照空格和.来分割字符串
String[] words = str.split("\\s+|\\.");
int[] wordLength = new int[words.length+1];
//确定每个单词的长度,方便后面计算距离
for (int i = 0; i < words.length; i++) {
wordLength[i] = words[i].length();
}
int ans = 0;
//根据Alice确定可行的Bob对数
for (int i = 0; i < words.length; i++) {
if ("Alice".equals(words[i])) {
for (int j = i+1; j < words.length; j++) {
int cnt = 1;
if ("Bob".equals(words[j])) {
for (int k = i+1; k < j; k++) {
cnt += (wordLength[k]+1);
}
if (cnt <= K)
ans++;
}
}
}
}
//根据Bob确定可行的Alice对数
for (int i = 0; i < words.length; i++) {
if ("Bob".equals(words[i])) {
for (int j = i+1; j < words.length; j++) {
int cnt = 1;
if ("Alice".equals(words[j])) {
for (int k = i+1; k < j; k++) {
cnt += (wordLength[k]+1);
}
if (cnt <= K)
ans++;
}
}
}
}
System.out.println(ans);
}
}
I - 后缀表达式
答案解析
- 一开始想当然的将数组从大到小排序,然后加上n个最大的数之后,减去剩下的数,但其实这种想法是错误的。比如数据:1 3 5 4 3 2 1,如果按照这思路,答案是5 + 4 - 3 - 2 - 1 = 3,而正确答案是 5 + 4 - (1 - 2 - 3) = 13。
- 题目让我们自己构造后缀表达式,也就是说我们可以随便改变n+m+1个数的运算顺序,即我们可以在对应的中缀表达式上任意加括号。
- 如果只有加减操作,那么我们要尽量使负号变为正号,即尽量减少减号的个数。
- 如果不存在负数:因为负负得正,所以为了使结果最大,我们可以用一个减号使其他m-1个减号变为正号。即我们要构造出这样的中缀表达式:A + B - (C - D - E - F - G),也就是:A + B + D + E + F + G - C,即只需要减去一个最小的数。
- 如果存在负数:直接减去负数,使其变为正数。如果减号不够,就将其加入括号中。反正最后无论怎么样都可以构成这样的式子:A + B + D + E + F + G - C
- 只有一个数的正负不能被改变,就是A,因为只要有减号,就不能没有被减数。为了使结果最大,A要确保是最大数。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int len = n+m+1;
long[] num = new long[len+1];
for (int i = 0; i<len;i++) {
num[i] = sc.nextInt();
}
long ans = 0;
if (m==0) {
for (int i = 0; i <len;i++) {
ans += num[i];
}
} else {
int maxx = 0, minn = 0;
for (int i =0 ;i<len;i++) {
if (num[i] > num[maxx])
maxx = i;
if (num[i] < num[minn])
minn = i;
}
ans = num[maxx]-num[minn];
for (int i = 0; i<len;i++) {
if (i != minn && i != maxx) {
ans += (Math.abs(num[i]));
}
}
}
System.out.println(ans);
}
}