目录
A_门牌制作
B_寻找连续2020个数
C_蛇形填数
D_七段码
E_字符串冒泡
F_成绩分析
G_单词分析
H_数字三角形
I_子串分值和
A_门牌制作
题目
(1)门牌号从 1 到 2020 编号。先制作 0 到 9 这几个数字字符
(2)例如门牌 1017 需要依次粘贴字符 1、0、1、7,即需要 1 个 字符 0,2 个字符 1,1 个字符 7。
问: 请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符 2?
方法一
/*思路一:
(1)遍历1-2020,转化为字符数组
(2)统计每个字符数组中字符'2'的个数即可
*/
public class _01_A_门牌制作 {
@Test
public void test1() {
int count = 0;
for (int i = 1; i <= 2020; i++) {
char[] chars = (i + "").toCharArray();
for (int j = 0; j < chars.length; j++) {
if (chars[j] == '2') count++;
}
}
System.out.println("总共需要2的个数为: " + count); // 624
}
}
方法二
/*思路二:
(1)遍历1-2020,!!!用 x % 10 == 2,
(2)统计每个数字2的个数
*/
public class _01_A_门牌制作2 {
@Test
public void test1() {
int count = 0;
for (int i = 1; i <= 2020; i++) {
int x = i;
while (x != 0) {
if (x % 10 == 2) count++; // 如果个位是2,则更新统计count
x = x / 10; // 去除个位
}
}
System.out.println("总共需要2的个数为: " + count); // 624
}
}
方法三
/*思路三:
(1)统计所有数字1-2020的个位总个数 + 十位总个数 + 百位总个数 + 千位总个数
(2)统计每个数字2的个数
*/
public class _01_A_门牌制作3 {
@Test
public void test1() {
int count = 0;
// 高位 当前位 低位 位因子
int high = 2020 / 10;
int cur = 2020 % 10;
int low = 0;
int digit = 1;
while (high != 0 || cur != 0) {
if (cur == 0) count += high * digit;
else if (cur == 2) count += high * digit + low + 1;
else if (cur != 2) count += (high + 1) * digit;
// 更新 high cur low digit
low += cur * digit; // 将cur将入到low,组成下一轮的low
cur = high % 10; // cur 前进一格
high = high / 10; // high 前进一格
digit = digit * 10; // 位因子每轮 * 10
}
System.out.println("总共需要2的个数为: " + count);
}
}
B_寻找连续2020个数
题目
小蓝有一个数字矩阵,里面只包含数字 0 和 2。小蓝很喜欢 2020,他想找到这个数字矩阵中有多少个 2020 。
小蓝只关注三种构成 2020 的方式:
• 同一行里面连续四个字符从左到右构成 2020。
• 同一列里面连续四个字符从上到下构成 2020。
• 在一条从左上到右下的斜线上连续四个字符,从左上到右下构成 2020。
例如,对于下面的矩阵:
6
220000
000000
002202
000000
000022
002020
一共有 5 个 2020。其中 1 个是在同一行里的,1 个是在同一列里的,3 个是斜线上的。
/*思路一:
(1)双重for循环遍历,进行3次
*/
public class _02_B_寻找连续2020个数 {
public static void main(String[] args) {
// ==============处理输入输出==================
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] arr = new int[n][n];
for (int i = 0; i < n; i++) {
String str = sc.next();
for (int j = 0; j < str.length(); j++) {
arr[i][j] = str.charAt(j) - '0';
}
}
// ==============业务逻辑==================
//System.out.println("test");
/* 1.连续左 -> 右*/
int count = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j + 3 < n && arr[i][j] == 2 && arr[i][j + 1] == 0
&& arr[i][j + 2] == 2 && arr[i][j + 3] == 0) {
count++;
}
}
}
/* 2.连续上 -> 下*/
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i + 3 < n && arr[i][j] == 2 && arr[i + 1][j] == 0
&& arr[i + 2][j] == 2 && arr[i + 3][j] == 0) {
count++;
}
}
}
/* 1.连续左上 -> 右下*/
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i + 3 < n && j + 3 < n && arr[i][j] == 2 && arr[i + 1][j + 1] == 0
&& arr[i + 2][j + 2] == 2 && arr[i + 3][j + 3] == 0) {
count++;
}
}
}
System.out.println(count);
}
}
C_蛇形填数
题目
1 2 6 7 15 ...
3 5 8 14 ...
4 9 13 ...
10 12 ...
11 ...
问: 容易看出矩阵第二行第二列中的数是 5。请你计算矩阵中第 20 行第 20 列的数是多少?
/* 思路
(1) 容易看出对角线 1 --- 1的基础上加上(2 * 2) --- 5 --- 5的基础上加上(4 * 2) --- 13
*/
public class _03_C_蛇形填数 {
public static void main(String[] args) {
int res = 1;
int digit = 2; // 因子
for (int i = 0; i < 20 - 1; i++) {
res += digit * 2;
digit += 2;
}
System.out.println(res);
}
}
D_七段码
题目
--a--
| |
f b
| |
--g--
| |
e c
| |
--d--
上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二 极管,分别标记为 a, b, c, d, e, f, g。
小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符 的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光 的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?
/*思路一:
(1)总共可能有长度为1..n的情况,对每种情况开启dfs的递归找到存在的所有情况
(2)根据所有边的连通情况来 ==构建==> 并查集pre,若发现所有节点的根节点都一样,则说明连通
*/
public class _04_D_七段码 {
static int count = 0;
static boolean[][] connect; // 连通关系
static int[] a; // 七段码转化为数字
public static void main(String[] args) {
// 1.初始化连通关系 a,b,c,d,e,f,g
// 0,1,2,3,4,5,6
connect = new boolean[7][7];
connect[0][1] = connect[1][0] = connect[0][5] = connect[5][0] = true;
connect[1][2] = connect[2][1] = connect[1][6] = connect[6][1] = true;
connect[2][3] = connect[3][2] = connect[2][6] = connect[6][2] = true;
connect[4][3] = connect[3][4] = true;
connect[4][5] = connect[5][4] = connect[4][6] = connect[6][4] = true;
connect[5][6] = connect[6][5] = true;
// 2.初始化a数组
a = new int[7];
for (int i = 0; i < 7; i++) {
a[i] = i;
}
// 3.开启dfs
for (int n = 1; n <= 7; n++) {
dfs(0, n); // 总共选取n个数字,从第一个数字开启深度遍历
}
System.out.println(count);
}
private static void dfs(int k, int n) {
// 1.递归出口
if (k == n) {
// 1.1 只有一个元素,那么肯定是合法的数码管
if (n == 1) {
count++;
return;
}
// 1.1 并查集方法检测是否连通
int[] pre = new int[n];
for (int i = 0; i < n; i++) {
pre[i] = i; // 自己的根节点(父亲/上一个元素)是自己
}
// 1.2 根据所有边的连通情况来 ==构建==> 并查集
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 如果节点a[i]和节点a[j]是连通的,那么将他们join连接起来,更新并查集
if (connect[a[i]][a[j]]) join(pre, i, j);
}
}
// 1.3 如果全部是连通的,那么pre[]的每个元素的根节点都是相同的
boolean flag = true;
for (int i = 1; i < n; i++) {
if (find(pre,0) != find(pre, i)) {
flag = false;
break;
}
}
if (flag) count++;
return;
}
// 2.递推
for (int i = k; i < a.length; i++) {
if (k == 0 || a[i] > a[k - 1]) {
swap(a, i, k);
dfs(k + 1, n);
swap(a, i, k);
}
}
}
// 两个结点相并
public static void join(int[] pre, int x, int y) {
int fx = find(pre, x);
int fy = find(pre, y);
// 两个结点属于不同图,相并
if (fx != fy) {
pre[fy] = fx;
}
}
private static int find(int[] pre, int node) {
int son = node;
// 1.查找根节点
while (node != pre[node]) { // 有父节点,继续寻找
node = pre[node];
}
// 2.路径优化
// 路径优化
/*int temp = 0;
while (son != node) {
temp = pre[son];
// 直接通跟
pre[son] = node;
// son向上走一格
son = pre[son];
}*/
return node;
}
private static void swap(int[] a, int i, int k) {
int t = a[i];
a[i] = a[k];
a[k] = t;
}
}
E_字符串冒泡
题目
(1)冒泡排序中,每次只能交换相邻的两个元素。对一个字符串中的字符排序,只允许交换相邻的两个字符
(2)则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。
4213: 四次交换 2413 2143 2134 1234
(3)问:字符串排序,正好需要 100 次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个
(4)冒泡排序交换n-1次 n-2次....1次,所以最后是一个等差数列, 总次数 = (n-1)*n/2
(5)因为要让字符串长度最小,所以逆序可以让交换次数最大化 ===> 逆序的字符串
(6)nomlkjihgfedcba
(7)当n = 15, 总次数为105次 > 100次
(8)需要再交换5次,为了使得字典序最小,则将第六个字符移动到首位 ==> inomlkjhgfedcba
F_成绩分析
题目
请计算这次考试的最高分、最低分和平均分。
【输入格式】
输入的第一行包含一个整数 n,表示考试人数。
接下来 n 行,每行包含一个 0 至 100 的整数,表示一个学生的得分。
7
80
92
56
74
88
99
10
【输出格式】
输出三行。
第一行包含一个整数,表示最高分。
第二行包含一个整数,表示最低分。
第三行包含一个实数,四舍五入保留正好两位小数,表示平均分。
99
10
71.29
public class _06_F_成绩分析 {
public static void main(String[] args) {
/*==============处理输入输出=============*/
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] scores = new int[n];
for (int i = 0; i < n; i++) {
scores[i] = sc.nextInt();
}
/*==============业务逻辑=============*/
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
double sum = 0;
for (int i = 0; i < n; i++) {
max = Math.max(scores[i], max);
min = Math.min(scores[i], min);
sum += scores[i];
}
System.out.println(max + "\n" + min + "\n" + String.format("%.2f", sum / n));
}
}
G_单词分析
题目
输入一行包含一个单词,单词只由小写英文字母组成。找到出现最多的字母和这个字母出现的次数。
【样例输入】lanqiao
【样例输出】
a
2
方法一
/* 思路
(1)使用hashmap来统计次数,其中key-value代表map<字符,出现次数>
*/
public class _07_G_单词分析 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
char[] chars = sc.next().toCharArray();
// 1.使用hashmap来统计次数
Map map = new HashMap<>(); // map<字符,出现次数>
for (int i = 0; i < chars.length; i++) {
map.put(chars[i], map.getOrDefault(chars[i], 0) + 1);
}
// 2.找出最大的次数
Set set = map.keySet();
char ch = '0';
int maxCount = -1;
for (Character c : set) {
if (map.get(c) > maxCount) {
maxCount = map.get(c);
ch = c;
}
}
System.out.println(ch);
System.out.println(maxCount);
}
}
方法二
/* 思路二
(1)使用数组来统计
*/
public class _07_G_单词分析2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
char[] chars = sc.next().toCharArray();
// 1.使用数组来统计次数
int[] letter = new int[26];
for (int i = 0; i < chars.length; i++) {
letter[chars[i] - 'a']++;
}
// 2.找出最大的次数
char ch = '0';
int maxCount = -1;
for (int i = 0; i < 26; i++) {
if (letter[i] > maxCount) {
maxCount = letter[i];
ch = (char) (i + 'a');
}
}
System.out.println(ch);
System.out.println(maxCount);
}
}
H_数字三角形
题目
(1)上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。找到路径最大的和。
(2) 规则
--每一步只能从走到下一层和它最近的左边的那个数或者右边的那个数。
--向左下走的次数与向右下走的次数相差不能超过 1。(也就是会走到中间)
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
/*思路
(1)DP动态规划,状态定义: dp[i][j]代表数字三角形第i行第j列位置的路径和的最大值,注意是是行不是索引
(2)由于每一步只能走到下一层的左边那个数或者右边的那个数,所以状态转移方程可以这么写
arr[i][j] += Math.max(arr[i - 1][j - 1], arr[i - 1][j]);
(3)初始状态dp[0][0]就是没有元素,则为0,当我们初始化数组就是默认为0
*/
public class _08_H_数字三角形 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] arr = new int[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
arr[i][j] = sc.nextInt();
// max(左上角,上一个)中取大的
arr[i][j] += Math.max(arr[i - 1][j - 1], arr[i - 1][j]);
}
}
System.out.println((n % 2 == 1) ? arr[n][n / 2 + 1] : Math.max(arr[n][n / 2], arr[n][n / 2 + 1]));
}
}
I_子串分值和
题目
(1)对于一个字符串 S,我们定义 S 的分值 f(S ) 为 S 中出现的不同的字符个数。例如 f(”aba”) = 2,f(”abc”) = 3, f(”aaa”) = 1。
(2)现在给定一个字符串 S [0..n − 1](长度为 n),请你计算对于所有 S 的非空子串 S [i.. j](0 ≤ i ≤ j < n),f(S [i.. j]) 的和是多少。
【样例输入】 ababc
【样例输出】 28
/* 思路
(1)双重for循环找出所有子串的情况
(2)对每种子串利用set进行去重的字符char统计,更新count
*/
public class _09_I_子串分值和 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.next();
int len = str.length();
int count = 0;
for (int i = 0; i < len; i++) {
for (int j = i; j < len; j++) {
HashSet set = new HashSet<>();
char[] chars = str.substring(i, j + 1).toCharArray();
for (int k = 0; k < chars.length; k++) {
set.add(chars[k]);
}
count += set.size();
}
}
System.out.println(count);
}
}