题目描述
一个应用启动时,会有多个初始化任务需要执行,并且任务之间有依赖关系,例如A任务依赖B任务,那么必须在B任务执行完成之后,才能开始执行A任务。
现在给出多条任务依赖关系的规则,请输入任务的顺序执行序列,规则采用贪婪策略,即一个任务如果没有依赖的任务,则立刻开始执行,如果同时有多个任务要执行,则根据任务名称字母顺序排序。
例如:B任务依赖A任务,C任务依赖A任务,D任务依赖B任务和C任务,同时,D任务还依赖E任务。那么执行任务的顺序由先到后是:
A任务,E任务,B任务,C任务,D任务
这里A和E任务都是没有依赖的,立即执行。
输入描述
输入参数每个元素都表示任意两个任务之间的依赖关系,输入参数中符号"->"表示依赖方向,例如:
A->B:表示A依赖B
多个依赖之间用单个空格分隔
输出描述
输出排序后的启动任务列表,多个任务之间用单个空格分隔
用例
输入
A->B C->B
输出
B A C
要解决这个任务调度问题,可以使用拓扑排序(Topological Sorting),这是一种在有向无环图(DAG)中排序顶点的方法,以确保每个顶点都在其所有依赖顶点之后。
下面是如何使用 Java 来实现这个问题的解决方案:
解决方案步骤
- 解析输入:将任务依赖关系转化为图的邻接表和入度表。
- 执行拓扑排序:使用贪婪策略(优先队列)来进行排序。
Java 实现
import java.util.*;
public class TaskScheduler {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
scanner.close();
// 1. Parse input
Map<Character, List<Character>> graph = new HashMap<>();
Map<Character, Integer> inDegree = new HashMap<>();
Set<Character> allTasks = new HashSet<>();
for (String relation : input.split(" ")) {
char from = relation.charAt(0);
char to = relation.charAt(3);
// Initialize graph and inDegree map
graph.putIfAbsent(from, new ArrayList<>());
graph.putIfAbsent(to, new ArrayList<>());
inDegree.putIfAbsent(from, 0);
inDegree.putIfAbsent(to, 0);
// Build graph and inDegree count
graph.get(from).add(to);
inDegree.put(to, inDegree.get(to) + 1);
allTasks.add(from);
allTasks.add(to);
}
// 2. Topological sort using a priority queue for lexicographical order
PriorityQueue<Character> zeroInDegreeQueue = new PriorityQueue<>();
for (char task : allTasks) {
if (inDegree.get(task) == 0) {
zeroInDegreeQueue.offer(task);
}
}
StringBuilder result = new StringBuilder();
while (!zeroInDegreeQueue.isEmpty()) {
char current = zeroInDegreeQueue.poll();
result.append(current).append(" ");
for (char neighbor : graph.get(current)) {
inDegree.put(neighbor, inDegree.get(neighbor) - 1);
if (inDegree.get(neighbor) == 0) {
zeroInDegreeQueue.offer(neighbor);
}
}
}
// Output result
System.out.println(result.toString().trim());
}
}
解释
-
输入解析:
- 通过分割输入字符串并解析依赖关系来构建图和入度表。
graph
是一个邻接表,表示每个任务的依赖任务。inDegree
是一个入度表,记录每个任务的依赖数。
-
拓扑排序:
- 使用一个优先队列(
PriorityQueue
)来保证每次取出的任务是字母顺序的最小值。 - 从入度为 0 的任务开始处理,每次从队列中取出一个任务,更新其所有依赖任务的入度。如果依赖任务的入度变为 0,则将其加入队列。
- 使用一个优先队列(
-
输出结果:
- 输出排序后的任务列表。
这种方法可以处理复杂的依赖关系,并保证任务执行的正确顺序。
题目描述
有 N 块二手市场收集的银饰,每块银饰的重量都是正整数,收集到的银饰会被熔化用于打造新的饰品。 每一回合,从中选出三块 最重的 银饰,然后一起熔掉。假设银饰的重量分别为 x 、y 和 z,且 x <= y <= z。那么熔掉的可能结果如下:
如果x == y == z,那么三块银饰都会被完全熔掉;
如果x == y且y != z,会剩余重量为z - y的银块无法被熔掉;
如果x != y且y == z,会剩余重量为y - x的银块无法被熔掉;
如果x != y且y != z,会剩余重量为z - y与y - x差值的银块无法被熔掉。
如果剩余两块,返回较大的重量(若两块重量相同,返回任意一块皆可);如果只剩下一块,返回该块的重量;如果没有剩下,就返回 0。
输入描述
输入数据为两行
第一行为银饰数组长度 n,1 ≤ n ≤ 40,
第二行为 n 块银饰的重量,重量的取值范围为[1,2000],重量之间使用空格隔开
输出描述
如果剩余两块,返回较大的重量(若两块重量相同,返回任意一块皆可);如果只剩下一块,返回该块的重量;如果没有剩下,就返回 0。
为了实现这个问题,我们需要一个贪婪算法来不断从银饰中选择最重的三块进行处理,直到没有足够的银饰可处理为止。以下是实现的详细步骤:
- 解析输入:将输入的银饰重量数据存储到一个列表中。
- 处理三块银饰:每次选择列表中最重的三块银饰进行处理,并根据题目规则更新银饰列表。
- 处理剩余银饰:在没有足够的银饰进行处理时,根据剩余银饰的数量决定返回的结果。
实现步骤
- 选择三块最重的银饰。
- 按照规则进行处理:
- 如果三块银饰的重量相同,则全部熔掉。
- 如果两块银饰的重量相同,剩余的重量需要被更新。
- 否则,计算并更新剩余的重量。
- 返回最终结果:
- 如果剩下两块,返回较大的重量。
- 如果只剩下一块,返回该块的重量。
- 如果没有剩余,返回0。
Java 实现代码
import java.util.*;
public class SilverProcessing {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取银饰的数量
int n = scanner.nextInt();
List<Integer> silverWeights = new ArrayList<>();
// 读取银饰的重量
for (int i = 0; i < n; i++) {
silverWeights.add(scanner.nextInt());
}
scanner.close();
// 持续处理银饰直到剩余不足三块
while (silverWeights.size() >= 3) {
// 对银饰重量进行排序
Collections.sort(silverWeights);
// 取出最重的三块银饰
int z = silverWeights.remove(silverWeights.size() - 1);
int y = silverWeights.remove(silverWeights.size() - 1);
int x = silverWeights.remove(silverWeights.size() - 1);
if (x == y && y == z) {
// 情况1: x == y == z
// 不需要做任何事情,三块银饰都被完全熔掉
} else if (x == y) {
// 情况2: x == y && y != z
if (z - y > 0) {
silverWeights.add(z - y);
}
} else if (y == z) {
// 情况3: x != y && y == z
if (y - x > 0) {
silverWeights.add(y - x);
}
} else {
// 情况4: x != y && y != z
int diff1 = z - y;
int diff2 = y - x;
if (diff1 > 0) {
silverWeights.add(diff1);
}
if (diff2 > 0) {
silverWeights.add(diff2);
}
}
// 对更新后的银饰列表排序
Collections.sort(silverWeights);
}
// 输出剩余银饰的重量
if (silverWeights.size() == 2) {
System.out.println(Math.max(silverWeights.get(0), silverWeights.get(1)));
} else if (silverWeights.size() == 1) {
System.out.println(silverWeights.get(0));
} else {
System.out.println(0);
}
}
}
代码解释
- 输入处理:从标准输入读取银饰的数量和重量,并存储在
silverWeights
列表中。 - 处理银饰:
- 排序银饰列表,取出最重的三块。
- 根据不同的规则更新银饰列表。
- 将剩余的重量重新插入列表并排序。
- 结果输出:根据剩余银饰的数量决定输出。
注意事项
- 代码使用了
Collections.sort()
进行排序以获取最重的三块银饰。 - 当列表的大小小于三时,退出处理并根据剩余的数量输出结果。
题目描述:
特定大小的停车场,数组cars[]表示,其中1表示有车,0表示没车。车辆大小不一,小车占一个车位(长度1),货车占两个车位(长度2),卡车占三个车位(长度3),统计停车场最少可以停多少辆车,返回具体的数目。
输入描述:
整型字符串数组cars[],其中1表示有车,0表示没车,数组长度小于1000。
输出描述:
整型数字字符串,表示最少停车数目。
补充说明:
示例1
输入:
1,0,1
输出:
2
说明:
1个小车占第1个车位
第二个车位空
1个小车占第3个车位
最少有两辆车
示例2
输入:
1,1,0,0,1,1,1,0,1
输出:
3
说明:
1个货车占第1、2个车位
第3、4个车位空
1个卡车占第5、6、7个车位
第8个车位空
1个小车占第9个车位
最少3辆车
为了计算停车场中最少可以停多少辆车,我们需要考虑不同大小的车辆(小车、货车、卡车)的停放规则,并尽可能利用停车位以最小化车辆数量。具体思路如下:
- 解析输入:将停车场的车位状态转换为整数数组。
- 车辆停放策略:
- 优先放置最大的车辆(卡车),因为它们需要更多的连续空间。
- 接着放置中等大小的车辆(货车)。
- 最后放置最小的车辆(小车)。
- 计算停车位使用情况:
- 遍历数组,从左到右检测连续的空位,并尝试放置尽可能大的车辆。
- 更新停车位状态,继续处理剩余的空位。
Java 实现代码
import java.util.Arrays;
public class ParkingLot {
public static void main(String[] args) {
String input = "1,1,0,0,1,1,1,0,1";
int[] cars = Arrays.stream(input.split(","))
.mapToInt(Integer::parseInt)
.toArray();
System.out.println(minimumCars(cars));
}
public static int minimumCars(int[] cars) {
int count = 0;
int n = cars.length;
int i = 0;
while (i < n) {
if (cars[i] == 1) {
// Find the length of the continuous segment of 1s
int start = i;
while (i < n && cars[i] == 1) {
i++;
}
int length = i - start;
// First try to place the largest vehicle (size 3)
if (length >= 3) {
count += length / 3;
length %= 3;
}
// Then try to place the medium vehicle (size 2)
if (length >= 2) {
count += length / 2;
length %= 2;
}
// Finally place the smallest vehicle (size 1)
if (length == 1) {
count += 1;
}
} else {
i++;
}
}
return count;
}
}
代码解释
-
输入处理:
- 将输入的字符串按逗号分隔,并转换为整数数组
cars
。
- 将输入的字符串按逗号分隔,并转换为整数数组
-
处理停车位:
- 使用
while
循环遍历cars
数组,找到连续的1
代表的空位段。 - 计算该段的长度,并依次尝试放置最大的车辆(卡车)、中等车辆(货车)、最小车辆(小车)。
- 更新剩余的空位长度,继续处理下一个段落。
- 使用
-
输出结果:
- 返回计算出的最少车辆数。
示例说明
- 对于输入
1,1,0,0,1,1,1,0,1
:- 找到连续的
1
段:1,1
(货车),1,1,1
(卡车),1
(小车)。 - 最终需要的车辆数量是:1 货车 + 1 卡车 + 1 小车 = 3。
- 找到连续的
这种方法保证了通过贪婪策略最小化车辆数量,并且适用于长度不超过 1000 的停车场数组。
题目描述
输入字符串s,输出s中包含所有整数的最小和。
说明:
字符串s,只包含 a-z A-Z ± ;
合法的整数包括
1) 正整数 一个或者多个0-9组成,如 0 2 3 002 102
2)负整数 负号 – 开头,数字部分由一个或者多个0-9组成,如 -0 -012 -23 -00023
输入描述
包含数字的字符串
输出描述
所有整数的最小和
用例
输入
bb1234aa
输出
10
说明
无
输入
bb12-34aa
输出
-31
说明
1+2+(-34) = -31
为了处理这个问题,我们需要从字符串中提取所有的整数,然后计算这些整数的总和。以下是解决此问题的详细步骤和 Java 实现代码:
解决步骤
-
提取整数:
- 使用正则表达式从字符串中提取所有的整数。正则表达式可以用来匹配正整数和负整数。
-
转换和累加:
- 将提取出的字符串形式的整数转换为实际的整数类型。
- 计算这些整数的和。
Java 实现代码
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MinimumSumOfIntegers {
public static void main(String[] args) {
// 示例输入
String input = "bb12-34aa";
System.out.println(minimumSum(input));
}
public static int minimumSum(String s) {
// 使用正则表达式提取所有整数,包括负整数
Pattern pattern = Pattern.compile("[-]?\\d+");
Matcher matcher = pattern.matcher(s);
int sum = 0;
// 遍历匹配到的整数
while (matcher.find()) {
// 将匹配到的字符串转换为整数
int number = Integer.parseInt(matcher.group());
// 累加到总和
sum += number;
}
return sum;
}
}
代码解释
-
正则表达式:
[-]?\\d+
:匹配负号(可选)后面跟随的一个或多个数字。这样可以提取正整数和负整数。
-
提取和计算:
Pattern
和Matcher
类用于从字符串中提取符合正则表达式的部分。matcher.find()
用于逐一查找匹配的整数,并用matcher.group()
提取匹配的字符串。Integer.parseInt()
将匹配的字符串转换为整数并累加到sum
。
示例解析
-
输入:
"bb12-34aa"
- 匹配到的整数为
12
和-34
。 - 计算和为
12 + (-34) = -22
。
- 匹配到的整数为
-
输入:
"bb1234aa"
- 匹配到的整数为
1234
。 - 计算和为
1234
。
- 匹配到的整数为
这个方法能够处理输入字符串中所有合法的整数,确保了对整数的正确提取和计算。
题目描述
提取字符串中的最长合法简单数学表达式,字符串长度最长的,并计算表达式的值。
如果没有,则返回0。 简单数学表达式只能包含以下内容:0-9数字,符号 +-*
说明:
-
所有数字,计算结果都不超过long
-
如果有多个长度一样的,请返回第一个表达式的结果
-
数学表达式,必须是最长的,合法的
-
操作符不能连续出现,如 +--+1 是不合法的
输入
字符串
输出
表达式值
样例输入
1-2abcd
样例输出
-1
要解决这个问题,我们需要从给定的字符串中提取所有合法的数学表达式,找到最长的那个,然后计算它的值。以下是解决该问题的详细步骤和 Java 实现代码:
解决步骤
-
提取合法数学表达式:
- 使用正则表达式从字符串中提取合法的数学表达式。
- 合法的数学表达式只能包含数字、加号(
+
)、减号(-
)和乘号(*
),且操作符不能连续出现。
-
验证表达式的合法性:
- 确保提取的表达式合法,没有连续的操作符。
-
计算表达式的值:
- 使用 Java 的表达式计算器
ScriptEngine
来计算数学表达式的值。
- 使用 Java 的表达式计算器
-
返回结果:
- 找到最长的合法表达式并计算其值。如果没有合法表达式,返回0。
Java 实现代码
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LongestMathExpression {
public static void main(String[] args) {
// 示例输入
String input = "1-2abcd";
System.out.println(evaluateLongestExpression(input));
}
public static long evaluateLongestExpression(String s) {
// 使用正则表达式提取所有合法的数学表达式
Pattern pattern = Pattern.compile("[0-9]+([+*/-][0-9]+)*");
Matcher matcher = pattern.matcher(s);
String longestExpression = "";
// 遍历匹配到的表达式
while (matcher.find()) {
String expr = matcher.group();
// 更新最长表达式
if (expr.length() > longestExpression.length()) {
longestExpression = expr;
}
}
if (longestExpression.isEmpty()) {
return 0; // 没有找到合法的表达式
}
// 计算最长合法表达式的值
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
return ((Number) engine.eval(longestExpression)).longValue();
} catch (ScriptException e) {
e.printStackTrace();
return 0; // 计算错误,返回0
}
}
}
代码解释
-
正则表达式:
"[0-9]+([+*/-][0-9]+)*"
:匹配由数字和操作符(+
,-
,*
,/
)组成的合法数学表达式,不允许操作符连续出现。
-
提取和更新最长表达式:
matcher.find()
用于查找所有符合条件的表达式。- 根据长度更新最长的合法表达式。
-
计算表达式的值:
- 使用
ScriptEngine
来计算表达式的值。ScriptEngine
是 Java 提供的一个功能,可以用来执行 JavaScript 代码,从而评估数学表达式。 - 处理
ScriptException
以确保计算错误时返回0。
- 使用
-
返回结果:
- 返回最长合法数学表达式的计算结果。如果没有找到合法表达式,则返回0。
示例解析
-
输入:
"1-2abcd"
- 匹配到的合法数学表达式为
1-2
。 - 计算
1-2
的结果为-1
。
- 匹配到的合法数学表达式为
-
输入:
"a1+2b-3*4"
- 匹配到的合法数学表达式为
1+2-3*4
。 - 计算
1+2-3*4
的结果为-9
。
- 匹配到的合法数学表达式为
题目描述
有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。
规则如下:
明文为一段数字串由 0~9 组成
密码本为数字 0~9 组成的二维数组
需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。
每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数宇。
如明文第 i 位 Data[i] 对应密码本单元格为 Book[x][y],则明文第 i 位对应的密文为X Y,X和Y之间用空格隔开。
如果有多条密文,返回字符序最小的密文。
如果密码本无法匹配,返回"error"。
请你设计这个加密程序。
示例1:
密码本:
0 0 2
1 3 4
6 6 4
明文:"3",密文:"1 1"
示例2:
密码本:
0 0 2
要解决这个问题,我们需要设计一个程序,根据明文串从密码本中找到对应的密文。我们要遍历密码本以找到明文的每个数字,并且根据规则输出其对应的坐标。如果没有找到匹配的密文,则返回 "error"
。为了确保匹配的结果按字典序最小,我们需要在遍历密码本时进行排序。
解决步骤
-
遍历密码本:
- 对密码本进行遍历,找到每个数字的位置。我们需要记录每个数字的位置,并且需要保证搜索过程尽可能快,以找到字典序最小的密文。
-
查找路径:
- 对于明文中的每个数字,尝试找到在密码本中相邻的数字序列。如果明文的数字串可以在密码本中找到相应的序列,那么记录下这些位置。
-
生成密文:
- 根据找到的路径生成密文。每个数字的位置被转化为
(行, 列)
的格式。如果有多条密文,选择字典序最小的。
- 根据找到的路径生成密文。每个数字的位置被转化为
-
返回结果:
- 如果找到了合法的密文,输出最小的密文。如果没有找到,返回
"error"
。
- 如果找到了合法的密文,输出最小的密文。如果没有找到,返回
Java 实现代码
import java.util.*;
public class Encryption {
public static void main(String[] args) {
// 示例输入
int[][] book = {
{0, 0, 2},
{1, 3, 4},
{6, 6, 4}
};
String plaintext = "3";
// 输出结果
System.out.println(findMinimumCipher(book, plaintext));
}
public static String findMinimumCipher(int[][] book, String plaintext) {
int rows = book.length;
int cols = book[0].length;
Map<Character, List<int[]>> positions = new HashMap<>();
// 记录每个数字的所有位置
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
char num = (char) (book[r][c] + '0');
if (!positions.containsKey(num)) {
positions.put(num, new ArrayList<>());
}
positions.get(num).add(new int[]{r, c});
}
}
// 从明文开始构建可能的密文
return findMinCipher(positions, plaintext, book);
}
private static String findMinCipher(Map<Character, List<int[]>> positions, String plaintext, int[][] book) {
List<int[]> currentPath = new ArrayList<>();
for (int i = 0; i < plaintext.length(); i++) {
char digit = plaintext.charAt(i);
if (!positions.containsKey(digit)) {
return "error";
}
List<int[]> possiblePositions = positions.get(digit);
if (currentPath.isEmpty()) {
currentPath.addAll(possiblePositions);
} else {
List<int[]> nextPath = new ArrayList<>();
for (int[] prev : currentPath) {
for (int[] pos : possiblePositions) {
if (isAdjacent(prev, pos)) {
nextPath.add(pos);
}
}
}
if (nextPath.isEmpty()) {
return "error";
}
Collections.sort(nextPath, (a, b) -> {
if (a[0] != b[0]) return a[0] - b[0];
return a[1] - b[1];
});
currentPath = nextPath;
}
}
if (currentPath.isEmpty()) {
return "error";
}
Collections.sort(currentPath, (a, b) -> {
if (a[0] != b[0]) return a[0] - b[0];
return a[1] - b[1];
});
StringBuilder sb = new StringBuilder();
for (int[] pos : currentPath) {
sb.append(pos[0]).append(" ").append(pos[1]).append(" ");
}
return sb.toString().trim();
}
private static boolean isAdjacent(int[] pos1, int[] pos2) {
int r1 = pos1[0], c1 = pos1[1];
int r2 = pos2[0], c2 = pos2[1];
return (r1 == r2 && Math.abs(c1 - c2) == 1) || (c1 == c2 && Math.abs(r1 - r2) == 1);
}
}
代码解释
-
记录数字位置:
- 使用
Map<Character, List<int[]>>
来记录每个数字在密码本中的所有位置。
- 使用
-
路径查找:
- 对于每个数字,从
plaintext
中提取的位置,检查是否可以连接到之前的数字。如果可以,将位置添加到nextPath
列表中。
- 对于每个数字,从
-
排序:
- 在每步中对路径进行排序,以确保生成的密文是字典序最小的。
-
计算结果:
- 生成最终的密文,格式为
行 列
,并返回。如果无法匹配任何密文,返回"error"
。
- 生成最终的密文,格式为
这段代码解决了如何在密码本中查找并生成合法的密文,同时确保结果是字典序最小的。
题目描述:
有一个文件, 包含以一定规则写作的文本, 请统计文件中包含的文本数量
规则如下
- 文本以";"分隔,最后一条可以没有";",但空文本不能算语句,比如"COMMAND A; ;"只能算一条语句.
注意, 无字符/空白字符/制表符都算作"空"文本
- 文本可以跨行, 比如下面, 是一条文本, 而不是三条
COMMAND A
AND
COMMAND B;
-
文本支持字符串, 字符串为成对的单引号(')或者成对的双引号("), 字符串可能出现用转义字符()处理的单双引号(比如"your input is: "")和转义字符本身, 比如 COMMAND A "Say "hello"";
-
支持注释, 可以出现在字符串之外的任意位置, 注释以"--"开头, 到换行结束, 比如
COMMAND A; -- this is comment
COMMAND -- comment
A AND COMMAND B;
注意, 字符串内的"--", 不是注释
输入描述:文本文件
输出描述:包含的文本数量
示例1
输入:COMMAND TABLE IF EXISTS "UNITED STATE";
COMMAND A GREAT (
ID ADSAB,
download_length INTE-GER, -- test
file_name TEXT,
guid TEXT,
mime_type TEXT
notifica-tionid INTEGER,
original_file_name TEXT,
pause_reason_type INTEGER,
resumable_flag INTEGER,
start_time INTEGER,
state INTEGER,
folder TEXT,
path TEXT,
total_length INTE-GER,
url TEXT
);
输出:2
要解决这个问题,我们需要处理包含文本的文件,按照给定的规则分割文本并计算有效的文本数量。下面是分步骤的解决方法:
- 读取文件内容:将文件中的所有内容读取到一个字符串中,处理跨行的情况。
- 去除注释:在处理文本之前,去除所有注释(
--
后到行尾的内容)。注意,注释不能出现在字符串内部。 - 处理字符串中的转义字符:确保正确解析字符串中的引号和转义字符。
- 按分隔符拆分文本:根据分隔符
;
拆分文本,并去除空文本。 - 统计有效文本:计算拆分后有效的文本数量。
以下是用 Java 实现这个过程的代码:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TextFileParser {
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Usage: java TextFileParser <file-path>");
return;
}
String filePath = args[0];
try {
String content = readFile(filePath);
String cleanedContent = removeComments(content);
int count = countValidTexts(cleanedContent);
System.out.println(count);
} catch (IOException e) {
e.printStackTrace();
}
}
// Read the entire file content
private static String readFile(String filePath) throws IOException {
StringBuilder content = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
content.append(line).append("\n");
}
}
return content.toString();
}
// Remove comments from the content
private static String removeComments(String content) {
StringBuilder result = new StringBuilder();
boolean inString = false;
boolean escaped = false;
for (int i = 0; i < content.length(); i++) {
char c = content.charAt(i);
if (c == '"' || c == '\'') {
if (!escaped) {
inString = !inString;
}
} else if (c == '\\') {
escaped = !escaped;
} else if (c == '-' && !inString && i + 1 < content.length() && content.charAt(i + 1) == '-') {
while (i < content.length() && content.charAt(i) != '\n') {
i++;
}
result.append('\n');
continue;
}
result.append(c);
}
return result.toString();
}
// Count valid texts separated by ';'
private static int countValidTexts(String content) {
String[] parts = content.split(";");
int count = 0;
for (String part : parts) {
String trimmed = part.trim();
if (!trimmed.isEmpty()) {
count++;
}
}
return count;
}
}
代码解释
-
读取文件内容:
- 使用
BufferedReader
读取文件的内容,将其合并为一个单独的字符串。
- 使用
-
去除注释:
- 在
removeComments
方法中,我们处理注释。处理逻辑包括判断是否在字符串内,处理注释行并将其删除。
- 在
-
拆分文本并统计:
- 在
countValidTexts
方法中,我们按分隔符;
拆分文本,去除空文本并计算有效文本的数量。
- 在
使用说明
- 将上述代码保存到一个 Java 文件中,例如
TextFileParser.java
。 - 编译代码:
javac TextFileParser.java
- 运行代码:
java TextFileParser <file-path>
,其中<file-path>
是文本文件的路径。
该代码会正确处理文件中的文本,去除注释,处理字符串中的转义字符,并计算有效文本的数量。
题目
RSA 加密算法在网络安全世界中无处不在, 它利用了极大整数因数分解的困难度,数据越大安全系数越高。 给定了一个 32 位正整数,请对其进行因数分解, 找出哪两个素数的乘积。
输入
一个正整数 num,满足 0 < num <= 2147483647。
输出描述
如果成功找到,则以单个空格分割, 从小到大输出两个素数。 分解失败请输出 -1 -1。
RSA 加密算法中的因数分解问题需要将一个给定的整数分解成两个素数的乘积。我们可以使用以下步骤来完成这个任务:
步骤
- 输入解析:读取输入的32位正整数。
- 寻找因数:
- 从最小的素数开始,检查是否能整除输入数字。
- 如果找到因数
p
,那么另一个因数q
计算为num / p
。 - 确保两个因数
p
和q
都是素数。
- 输出结果:
- 如果找到两个素数,则输出它们。
- 如果没有找到符合条件的因数,则输出
-1 -1
。
实现
以下是用 Java 实现的代码:
import java.util.Scanner;
public class RSADecoder {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
scanner.close();
int[] factors = findPrimeFactors(num);
if (factors != null) {
System.out.println(factors[0] + " " + factors[1]);
} else {
System.out.println("-1 -1");
}
}
// Find two prime factors of the number
private static int[] findPrimeFactors(int num) {
if (num <= 1) return null;
// Check for factors from 2 to sqrt(num)
for (int i = 2; i <= Math.sqrt(num); i++) {
if (num % i == 0) {
int p = i;
int q = num / i;
if (isPrime(p) && isPrime(q)) {
return new int[]{Math.min(p, q), Math.max(p, q)};
}
}
}
return null;
}
// Check if a number is prime
private static boolean isPrime(int number) {
if (number <= 1) return false;
if (number <= 3) return true;
if (number % 2 == 0 || number % 3 == 0) return false;
for (int i = 5; i * i <= number; i += 6) {
if (number % i == 0 || number % (i + 2) == 0) return false;
}
return true;
}
}
代码解释
-
findPrimeFactors
方法:- 遍历从 2 到
sqrt(num)
的所有数字,尝试找到一个因数i
,使得num % i == 0
。 - 计算另一个因数
q
为num / i
。 - 检查
p
和q
是否都是素数。如果是,则返回这两个素数。 - 如果没有找到满足条件的因数,则返回
null
。
- 遍历从 2 到
-
isPrime
方法:- 使用经典的素数检测算法,优化为检查到
sqrt(number)
。 - 先排除 2 和 3 的倍数,然后使用 6 的倍数优化检测过程。
- 使用经典的素数检测算法,优化为检查到
-
主程序:
- 从标准输入读取整数。
- 调用
findPrimeFactors
方法来找到并打印结果。
使用说明
- 将代码保存为
RSADecoder.java
。 - 编译代码:
javac RSADecoder.java
- 运行代码:
java RSADecoder
,并输入一个正整数。
该代码将有效地处理32位正整数,并找出其两个素数因子。如果无法找到这样的因子,则返回 -1 -1
。
题目描述
主管期望你来实现英文输入法单词联想功能,需求如下:
- 依据用户输入的单词前缀,从已输入的英文语句中联想出用户想输入的单词。
- 按字典序输出联想到的单词序列,如果联想不到,请输出用户输入的单词前缀。
注意:
- 英文单词联想时区分大小写
- 缩略形式如"don’t" 判定为两个单词 "don"和 “t”
- 输出的单词序列不能有重复单词,且只能是英文单词,不能有标点符号
输入
输入两行。
首行输入一段由英文单词word和标点构成的语句str,接下来一行为一个英文单词前缀pre。
0 < word.length() <= 20
0 < str.length() <= 10000,0 < pre.length() <= 20
输出
输出符合要求的单词序列或单词前缀。存在多个时,单词之间以单个空格分割
样例输入
I love you
He
样例输出
He
要实现一个英文输入法单词联想功能,我们可以遵循以下步骤:
步骤
-
输入处理:
- 读取一段由英文单词和标点符号组成的文本。
- 读取用户提供的前缀。
-
文本预处理:
- 从文本中提取所有的单词,排除标点符号。
- 处理单词的缩略形式,如"don’t"应被拆分为"don"和"t"。
-
单词联想:
- 根据提供的前缀,从提取的单词中找出以该前缀开头的单词。
- 确保返回的单词按字典序排序,并且没有重复。
-
输出结果:
- 如果找到符合条件的单词,则按字典序输出这些单词。
- 如果没有找到任何符合条件的单词,则输出用户输入的前缀。
实现代码(Java)
import java.util.*;
import java.util.regex.*;
public class WordSuggestion {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Read input
String text = scanner.nextLine();
String prefix = scanner.nextLine();
scanner.close();
// Extract words from the text
Set<String> words = extractWords(text);
// Find matching words
List<String> matches = new ArrayList<>();
for (String word : words) {
if (word.startsWith(prefix)) {
matches.add(word);
}
}
// Sort matches
Collections.sort(matches);
// Output results
if (matches.isEmpty()) {
System.out.println(prefix);
} else {
System.out.println(String.join(" ", matches));
}
}
// Method to extract words from text
private static Set<String> extractWords(String text) {
Set<String> words = new HashSet<>();
// Define a pattern for valid words, considering only alphabetic characters and apostrophes
Pattern pattern = Pattern.compile("[a-zA-Z]+(?:'[a-zA-Z]+)?");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
words.add(matcher.group());
}
return words;
}
}
代码解释
-
main
方法:- 读取用户输入的文本和前缀。
- 调用
extractWords
方法提取文本中的单词。 - 根据前缀筛选符合条件的单词,并排序。
- 输出符合条件的单词或前缀(如果没有找到匹配的单词)。
-
extractWords
方法:- 使用正则表达式提取文本中的单词。
- 处理英文单词及其缩略形式,如“don’t”被拆分为“don”和“t”。
- 结果存储在
HashSet
中以避免重复,并返回这个集合。
使用说明
- 将代码保存为
WordSuggestion.java
。 - 编译代码:
javac WordSuggestion.java
- 运行代码:
java WordSuggestion
,然后输入文本和前缀。
此代码会处理大文本输入并返回符合条件的单词列表,确保按字典序排列并去除重复项。如果没有找到符合条件的单词,则返回前缀。
题目描述
快递公司每日早晨,给每位快递员推送需要淡到客户手中的快递以及路线信息,快递员自己又查找了一些客户与客户之间的路线距离信息,请你依据这些信息,给快递员设计一条最短路径,告诉他最短路径的距离。
不限制快递包裹送到客户手中的顺序,但必须保证都送到客户手中;
用例保证一定存在投递站到每位客户之间的路线,但不保证客户与客户之间有路线,客户位置及投递站均允许多次经过;
所有快递送完后,快递员需回到投递站;
输入描述
首行输入两个正整数n、m.
接下来n行,输入快递公司发布的客户快递信息,格式为:客户id 投递站到客户之间的距离distance
再接下来的m行,是快递员自行查找的客户与客户之间的距离信息,格式为:客户1id 客户2id distance
在每行数据中,数据与数据之间均以单个空格分割规格:
0 <=n <= 10 0 <= m <= 10 0 < 客户id <= 1000 0 < distance <= 10000
输出描述
最短路径距离,如无法找到,请输出-1
示例1
输入:
2 1
1 1000
2 1200
1 2 300
输出:
2500
说明:
快递员先把快递送到客户1手中,接下来直接走客户1到客户2之间的直通线路,最后走投递站和客户2之间的路,回到投递站,距离为1000+300+1200= 2500
示例2
输入:
5 1
5 1000
9 1200
17 300
132 700
500 2300
5 9 400
输出:
9200
为了设计一个计算快递员最短路径的算法,我们可以使用图论中的最短路径算法。具体来说,可以使用以下步骤来解决这个问题:
步骤概述
-
图建模:
- 视图包括两部分:客户到投递站的距离信息和客户间的距离信息。
- 构建图来表示投递站、客户和客户之间的距离。
-
求解最短路径:
- 使用最短路径算法来找到从投递站到每个客户的最短路径,再从每个客户到其他客户的最短路径,最后回到投递站的路径。
-
生成路径:
- 使用旅行商问题 (TSP) 的算法来计算从投递站出发,访问所有客户并返回投递站的最短路径。
详细解法
-
输入处理:
- 解析客户到投递站的距离。
- 解析客户之间的距离。
-
图的建立:
- 创建一个完整的图,包含所有客户和投递站。
- 添加投递站到每个客户的边,以及客户间的边。
-
使用旅行商问题 (TSP) 算法:
- 使用动态规划(DP)或其他 TSP 解法来求解从投递站到所有客户并返回的最短路径。
实现代码(Java)
import java.util.*;
public class DeliveryRoute {
private static final int INF = Integer.MAX_VALUE;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Read the number of clients and customer-to-customer distances
int n = scanner.nextInt();
int m = scanner.nextInt();
scanner.nextLine(); // Move to next line
// Client to delivery station distances
int[] clientToStation = new int[1000];
Arrays.fill(clientToStation, INF);
// Reading client to delivery station distances
for (int i = 0; i < n; i++) {
int clientId = scanner.nextInt();
int distance = scanner.nextInt();
clientToStation[clientId] = distance;
}
scanner.nextLine(); // Move to next line
// Reading client-to-client distances
Map<Integer, Map<Integer, Integer>> distances = new HashMap<>();
for (int i = 0; i < m; i++) {
int client1 = scanner.nextInt();
int client2 = scanner.nextInt();
int distance = scanner.nextInt();
scanner.nextLine(); // Move to next line
distances.computeIfAbsent(client1, k -> new HashMap<>()).put(client2, distance);
distances.computeIfAbsent(client2, k -> new HashMap<>()).put(client1, distance);
}
// Collect all clients and include the delivery station as client 0
Set<Integer> clients = new HashSet<>(distances.keySet());
clients.addAll(distances.values().stream().flatMap(m -> m.keySet().stream()).toList());
clients.add(0); // Delivery station
List<Integer> clientList = new ArrayList<>(clients);
int clientCount = clientList.size();
// Initialize distances between all nodes
int[][] graph = new int[clientCount][clientCount];
for (int[] row : graph) {
Arrays.fill(row, INF);
}
// Fill the graph with known distances
for (int client : clients) {
if (client == 0) {
for (int c : clientList) {
if (c != 0) {
graph[clientList.indexOf(client)][clientList.indexOf(c)] = clientToStation[c];
graph[clientList.indexOf(c)][clientList.indexOf(client)] = clientToStation[c];
}
}
} else {
Map<Integer, Integer> edges = distances.get(client);
for (Map.Entry<Integer, Integer> entry : edges.entrySet()) {
graph[clientList.indexOf(client)][clientList.indexOf(entry.getKey())] = entry.getValue();
graph[clientList.indexOf(entry.getKey())][clientList.indexOf(client)] = entry.getValue();
}
}
}
// Use Held-Karp algorithm for TSP (Dynamic Programming)
int result = tsp(graph, 0);
// Print the result
System.out.println(result);
}
private static int tsp(int[][] graph, int start) {
int n = graph.length;
int[][] dp = new int[1 << n][n];
for (int[] row : dp) {
Arrays.fill(row, INF);
}
dp[1 << start][start] = 0;
for (int mask = 1; mask < (1 << n); mask++) {
for (int u = 0; u < n; u++) {
if ((mask & (1 << u)) == 0) continue;
for (int v = 0; v < n; v++) {
if ((mask & (1 << v)) != 0) continue;
dp[mask | (1 << v)][v] = Math.min(dp[mask | (1 << v)][v], dp[mask][u] + graph[u][v]);
}
}
}
int finalResult = INF;
for (int i = 0; i < n; i++) {
if (i != start) {
finalResult = Math.min(finalResult, dp[(1 << n) - 1][i] + graph[i][start]);
}
}
return finalResult == INF ? -1 : finalResult;
}
}
代码说明
-
输入处理:
- 读取客户与投递站的距离。
- 读取客户间的距离信息,并构建图。
-
图建模:
- 使用二维数组
graph
来表示所有节点之间的距离。 - 将客户到投递站的距离添加到图中。
- 使用二维数组
-
最短路径计算:
- 使用 Held-Karp 算法解决 TSP 问题来找到最短路径。此算法的时间复杂度为 O(n^2 * 2^n),适合处理小规模问题。
-
输出结果:
- 打印计算出的最短路径距离。如果没有合法路径,输出
-1
。
- 打印计算出的最短路径距离。如果没有合法路径,输出
这样可以高效地计算最短路径并处理多个客户和投递站的情况。
- 题目描述
程序员小明打了一辆出租车去上班。出于职业敏感,他注意到这辆出租车的计费表有点问题,总是偏大。
出租车司机解释说他不喜欢数字4,所以改装了计费表,任何数字位置遇到数字4就直接跳过,其余功能都正常。
比如:
23再多一块钱就变为25;
39再多一块钱变为50;
399再多一块钱变为500;
小明识破了司机的伎俩,准备利用自己的学识打败司机的阴谋。
给出计费表的表面读数,返回实际产生的费用
示例1:
输入 5
输出 4
说明: 5表示计费表的表面读数。4表示实际产生的费用其实只有4块钱。
示例2:
输入 17
输出 15
说明 : 17表示计费表的表面读数。15表示实际产生的费用其实只有15块钱。
示例3:
输入 100
输出 81
说明: 100表示计费表的表面读数。81表示实际产生的费用其实只有81块钱。
为了计算出租车的实际费用,我们需要处理一个特殊的计费表。计费表中遇到数字 4 的位置会被跳过。因此,我们需要根据这个规则,将计费表的表面读数转换为实际费用。
具体步骤如下:
-
理解问题:
- 计费表中的数字 4 被跳过,这意味着数字 4 在表面读数中的位置不会影响实际费用。
- 我们需要将表面读数转换为实际读数,去掉所有 4,并将剩余的数字作为实际费用。
-
方法:
- 将给定的表面读数转换为实际费用的方法是:逐位检查表面读数,跳过所有的 4,将其他数字拼接起来,得到实际费用。
实现步骤
-
读取输入:
- 输入是一个整数,表示计费表的表面读数。
-
处理数字:
- 将表面读数转换为字符串,遍历每一位,跳过所有的 4,将其他字符拼接成实际费用的字符串。
-
输出结果:
- 将拼接的结果转换回整数,输出实际费用。
Java 实现
import java.util.Scanner;
public class TaxiFare {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String surfaceReading = scanner.nextLine().trim();
String actualReading = processReading(surfaceReading);
System.out.println(actualReading);
}
private static String processReading(String surfaceReading) {
StringBuilder actualReading = new StringBuilder();
for (char c : surfaceReading.toCharArray()) {
if (c != '4') {
actualReading.append(c);
}
}
// Convert the result to a number and handle edge cases (e.g., if actualReading is empty)
if (actualReading.length() == 0) {
return "0"; // In case all digits are 4, return 0
}
return actualReading.toString();
}
}
代码解释
-
读取输入:
- 使用
Scanner
类读取输入的表面读数。
- 使用
-
处理表面读数:
- 将表面读数转换为字符数组,遍历每个字符。
- 如果字符不是 '4',则将其添加到
StringBuilder
对象中,最终构造出实际的读数。
-
输出实际费用:
- 将
StringBuilder
对象转换为字符串,并处理空字符串的情况(如果所有的数字都是 4,则返回 0)。
- 将
通过这种方法,我们可以准确地计算出租车的实际费用,从而揭示司机的伎俩。
题目描述
开头和结尾都是元音字母(aeiouAEIOU)的字符串为元音字符串,其中混杂的非元音字母数量为其瑕疵度。比如:
“a” 、 “aa”是元音字符串,其瑕疵度都为0
“aiur”不是元音字符串(结尾不是元音字符)
“abira”是元音字符串,其瑕疵度为2
给定一个字符串,请找出指定瑕疵度的最长元音字符子串,并输出其长度,如果找不到满足条件的元音字符子串,输出0。
子串:字符串中任意个连续的字符组成的子序列称为该字符串的子串。
输入描述
首行输入是一个整数,表示预期的瑕疵度flaw,取值范围[0, 65535]。
接下来一行是一个仅由字符a-z和A-Z组成的字符串,字符串长度(0, 65535]。
输出描述
输出为一个整数,代表满足条件的元音字符子串的长度。
示例:
输入 0
asdbuiodevauufgh
输出 3
说明 无
要解决这个问题,我们需要找到指定瑕疵度的最长元音字符串子串,并输出其长度。下面是处理这个问题的步骤和Java实现。
解决步骤
-
定义元音字符:
- 元音字符为
a, e, i, o, u
以及它们的大写字母形式A, E, I, O, U
。
- 元音字符为
-
查找所有元音子串:
- 遍历整个字符串,找到所有以元音字符开始和结束的子串。
-
计算瑕疵度:
- 对于每个可能的元音子串,计算其瑕疵度(即非元音字符的数量)。
-
找到最长满足条件的子串:
- 根据给定的瑕疵度,检查所有找到的元音子串中哪些符合条件,记录其中最长的子串长度。
-
输出结果:
- 如果找到了符合条件的子串,输出其长度;否则输出 0。
Java 实现
import java.util.HashSet;
import java.util.Set;
import java.util.Scanner;
public class VowelSubstring {
private static final Set<Character> VOWELS = new HashSet<>();
static {
char[] vowels = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'};
for (char c : vowels) {
VOWELS.add(c);
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int flaw = scanner.nextInt();
scanner.nextLine(); // Consume the newline
String input = scanner.nextLine();
int result = findLongestVowelSubstring(flaw, input);
System.out.println(result);
}
private static int findLongestVowelSubstring(int flaw, String str) {
int maxLength = 0;
int n = str.length();
for (int i = 0; i < n; i++) {
if (isVowel(str.charAt(i))) {
for (int j = i; j < n; j++) {
if (isVowel(str.charAt(j))) {
String substring = str.substring(i, j + 1);
int countFlaw = countNonVowels(substring);
if (countFlaw == flaw) {
maxLength = Math.max(maxLength, j - i + 1);
}
}
}
}
}
return maxLength;
}
private static boolean isVowel(char c) {
return VOWELS.contains(c);
}
private static int countNonVowels(String str) {
int count = 0;
for (char c : str.toCharArray()) {
if (!isVowel(c)) {
count++;
}
}
return count;
}
}
代码解释
-
初始化元音字符集合:
- 使用
HashSet
存储所有元音字符以便于快速查找。
- 使用
-
处理输入:
- 读取瑕疵度和输入字符串。
-
查找元音子串:
- 遍历字符串的每个可能的起始点。如果字符是元音,则开始查找可能的子串。
- 对每个子串计算瑕疵度,并更新最长子串长度。
-
输出结果:
- 输出最长符合条件的子串长度。如果没有找到符合条件的子串,则输出 0。
这种方法通过逐步检查每个可能的子串来确保能够找到符合条件的最长子串。尽管时间复杂度较高,但由于实际输入范围可能不超过 65535 的长度,效率是可以接受的。
题目描述
给定两个整数数组array1、array2,数组元素按升序排列。
假设从array1、array2中分别取出一个元素可构成一对元素,现在需要取出k对元素,并对取出的所有元素求和,计算和的最小值。
注意:两对元素如果对应于array1、array2中的两个下标均相同,则视为同一对元素。
输入描述
输入两行数组array1、array2,每行首个数字为数组大小size(0 < size <= 100);
0 <array1[i] <=1000
0 <array2[i] <= 1000
接下来一行为正整数 k
0 < k <= array1.size() * array2.size()
输出描述
满足要求的最小和
示例1
输入:
3 1 1 2
3 1 2 3
2
输出:
4
说明:
用例中,需要取2对元素
取第一个数组第0个元素与第二个数组第0个元素组成1对元素[1,1];
取第一个数组第1个元素与第二个数组第0个元素组成1对元素[1,1];
求和为1+1+1+1=4,为满足要求的最小和
要解决这个问题,我们需要从两个升序排列的整数数组中选择k
对元素,使得这些对元素的和的总和最小。下面是详细的解题步骤和Java实现。
解题步骤
-
定义问题:
- 我们需要从
array1
和array2
中选择k
对元素,使得这些对元素的和的总和最小。
- 我们需要从
-
生成所有可能的对:
- 对于每对
(array1[i], array2[j])
,计算它们的和,并存储这些和及其对应的对。
- 对于每对
-
排序和选择最小对:
- 将所有可能的对的和按升序排序,然后选择前
k
个最小的和,计算它们的总和。
- 将所有可能的对的和按升序排序,然后选择前
-
输出结果:
- 输出前
k
个最小和的总和。
- 输出前
Java 实现
import java.util.*;
public class MinSumPairs {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取数组array1
int size1 = scanner.nextInt();
int[] array1 = new int[size1];
for (int i = 0; i < size1; i++) {
array1[i] = scanner.nextInt();
}
// 读取数组array2
int size2 = scanner.nextInt();
int[] array2 = new int[size2];
for (int i = 0; i < size2; i++) {
array2[i] = scanner.nextInt();
}
// 读取k
int k = scanner.nextInt();
// 调用函数计算最小和
int result = findMinSum(array1, array2, k);
System.out.println(result);
}
private static int findMinSum(int[] array1, int[] array2, int k) {
int size1 = array1.length;
int size2 = array2.length;
// 优先队列(最小堆)用于保存当前最小的k个和
PriorityQueue<Pair> minHeap = new PriorityQueue<>(Comparator.comparingInt(p -> p.sum));
// 初始将每个array1的元素与array2的第一个元素组合起来
for (int i = 0; i < size1; i++) {
minHeap.offer(new Pair(array1[i] + array2[0], i, 0));
}
int sum = 0;
while (k-- > 0 && !minHeap.isEmpty()) {
Pair current = minHeap.poll();
sum += current.sum;
// 当前的pair来自array1[current.i] 和 array2[current.j],尝试将下一对放入堆中
if (current.j + 1 < size2) {
minHeap.offer(new Pair(array1[current.i] + array2[current.j + 1], current.i, current.j + 1));
}
}
return sum;
}
// 内部类,用于存储每对元素的和及其索引
private static class Pair {
int sum;
int i;
int j;
Pair(int sum, int i, int j) {
this.sum = sum;
this.i = i;
this.j = j;
}
}
}
代码解释
-
数据读取:
- 从输入中读取两个数组和
k
的值。
- 从输入中读取两个数组和
-
最小堆:
- 使用优先队列(最小堆)来存储当前最小的和。堆中的每个元素是一个
Pair
对象,包含和及对应的索引。
- 使用优先队列(最小堆)来存储当前最小的和。堆中的每个元素是一个
-
初始化堆:
- 将
array1
中的每个元素与array2
中的第一个元素组合,并插入堆中。
- 将
-
提取最小和:
- 从堆中提取最小和,并尝试将下一个可能的和(来自同一
array1
的元素和array2
中下一个元素的组合)插入堆中。
- 从堆中提取最小和,并尝试将下一个可能的和(来自同一
-
输出结果:
- 输出最小
k
个和的总和。
- 输出最小
复杂度分析
- 时间复杂度:
O(N log N + K log N)
,其中N
为array1
的大小,K
为需要选择的对的数量。 - 空间复杂度:
O(N)
,用于存储堆中的元素。
某学校举行运动会,学生们按编号(1、2、3…n)进行标识,现需要按照身高由低到高排列,对身高相同的人,按体重由轻到重排列;对于身高体重都相同的人,维持原有的编号顺序关系。请输出排列后的学生编号。
时间限制:限制时间C/C++ 1秒,其他/Java 2s以内
内存限制:32768K
示例 1
输入:
4
100 100 120 130
40 30 60 50
输出:
2134
要解决这个问题,我们需要根据学生的身高和体重对其进行排序,同时保持原有的编号顺序。以下是详细的解题步骤和Java实现代码。
解题步骤
-
读取输入数据:
- 读取学生数量
n
。 - 读取身高和体重信息。
- 学生的编号从1到n。
- 读取学生数量
-
创建学生对象:
- 每个学生可以用一个包含编号、身高和体重的对象表示。
-
排序:
- 首先按身高从低到高排序。
- 对于身高相同的学生,按体重从轻到重排序。
- 对于身高和体重都相同的学生,保持原有编号顺序。
-
输出排序后的编号:
- 输出按照排序规则排列后的学生编号。
Java 实现
import java.util.*;
public class StudentSort {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取学生数量
int n = scanner.nextInt();
// 读取身高
int[] heights = new int[n];
for (int i = 0; i < n; i++) {
heights[i] = scanner.nextInt();
}
// 读取体重
int[] weights = new int[n];
for (int i = 0; i < n; i++) {
weights[i] = scanner.nextInt();
}
// 创建学生对象列表
List<Student> students = new ArrayList<>();
for (int i = 0; i < n; i++) {
students.add(new Student(i + 1, heights[i], weights[i]));
}
// 排序
Collections.sort(students);
// 输出结果
for (Student student : students) {
System.out.print(student.id);
}
}
// 学生类,包含编号、身高和体重
static class Student implements Comparable<Student> {
int id;
int height;
int weight;
Student(int id, int height, int weight) {
this.id = id;
this.height = height;
this.weight = weight;
}
// 实现 Comparable 接口的 compareTo 方法
@Override
public int compareTo(Student other) {
if (this.height != other.height) {
return Integer.compare(this.height, other.height);
}
if (this.weight != other.weight) {
return Integer.compare(this.weight, other.weight);
}
return Integer.compare(this.id, other.id);
}
}
}
代码解释
-
数据读取:
- 通过
Scanner
读取输入的数据。 - 读取身高和体重数组,并存储在
heights
和weights
中。
- 通过
-
学生对象:
- 创建一个
Student
类,其中包含学生的编号、身高和体重。 - 在
Student
类中实现Comparable
接口,并重写compareTo
方法以按照题目要求排序:首先按身高排序,其次按体重排序,最后按编号排序(当身高和体重都相同)。
- 创建一个
-
排序:
- 使用
Collections.sort
对学生列表进行排序,这会调用Student
类中的compareTo
方法进行比较。
- 使用
-
输出:
- 输出排序后的学生编号,依次打印所有学生的编号。
复杂度分析
- 时间复杂度:
O(n log n)
,主要是排序操作。 - 空间复杂度:
O(n)
,用于存储学生对象列表。
这个解决方案在时间和空间复杂度上都能有效处理输入的大小,并且符合题目要求。
题目描述
绘图机器的绘图笔初始位置在原点(0,0)机器启动后按照以下规则来进行绘制直线。
-
尝试沿着横线坐标正向绘制直线直到给定的终点E
-
期间可以通过指令在纵坐标轴方向进行偏移,offsetY为正数表示正向偏移,为负数表示负向偏移
给定的横坐标终点值E 以及若干条绘制指令,
请计算绘制的直线和横坐标轴以及x=E的直线组成的图形面积。
输入描述
首行为两个整数 N 和 E
表示有N条指令,机器运行的横坐标终点值E
接下来N行 每行两个整数表示一条绘制指令x offsetY
用例保证横坐标x以递增排序的方式出现
且不会出现相同横坐标x
取值范围
0<N<=10000
0<=x<=E<=20000
-10000<=offsetY<=10000
输出描述
一个整数表示计算得到的面积 用例保证结果范围在0到4294967295之内。
用例1
输入
4 10
1 1
2 1
3 1
4 -2
输出
12
用例2
输入
2 4
0 1
2 -2
输出
4
为了计算绘图机器绘制的图形与横坐标轴以及终点直线 ( x = E ) 之间的面积,我们可以将问题建模为一个多边形面积计算问题。具体来说,我们需要计算由绘制的直线段以及终点 ( x = E ) 形成的图形的面积。
解题思路
-
数据解析:
- 读取输入的绘制指令,解析每个指令的横坐标 ( x ) 和纵坐标偏移量 ( \text{offsetY} )。
- 将这些指令用于构建在 ( x ) 轴上绘制的图形的轮廓。
-
多边形的面积计算:
- 使用多边形面积公式来计算由这些绘制指令形成的多边形的面积。
- 将绘制的线段视为多边形的一部分,该多边形由这些线段和终点 ( x = E ) 形成。
-
计算过程:
- 首先,构建一个多边形的顶点列表。顶点包括指令给定的点和终点 ( x = E ) 处的点。
- 使用Shoelace公式(也称为高斯面积公式)来计算多边形的面积。
详细步骤
-
构建顶点列表:
- 从原点 ((0, 0)) 开始。
- 遍历所有指令,记录每个指令的终点。
- 记录终点 ( x = E ) 对应的纵坐标。
-
应用Shoelace公式:
- 对多边形顶点进行排序,计算其面积。
Java 实现代码
import java.util.*;
public class DrawingMachine {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取 N 和 E
int N = scanner.nextInt();
int E = scanner.nextInt();
// 读取指令
List<int[]> commands = new ArrayList<>();
for (int i = 0; i < N; i++) {
int x = scanner.nextInt();
int offsetY = scanner.nextInt();
commands.add(new int[]{x, offsetY});
}
// 构建顶点列表
List<Point> points = new ArrayList<>();
points.add(new Point(0, 0)); // 起点
for (int[] cmd : commands) {
points.add(new Point(cmd[0], cmd[1]));
}
// 添加终点
points.add(new Point(E, commands.get(commands.size() - 1)[1]));
// 计算多边形面积
long area = calculatePolygonArea(points);
System.out.println(area);
}
static class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
// 使用Shoelace公式计算多边形的面积
private static long calculatePolygonArea(List<Point> points) {
long area = 0;
int n = points.size();
for (int i = 0; i < n; i++) {
Point p1 = points.get(i);
Point p2 = points.get((i + 1) % n);
area += p1.x * p2.y - p2.x * p1.y;
}
return Math.abs(area) / 2;
}
}
代码说明
-
数据读取:
- 从标准输入读取数据并解析。
-
顶点构建:
- 将所有绘制指令转化为顶点。
- 在最后加上终点 ( x = E ) 对应的点。
-
面积计算:
- 使用Shoelace公式计算多边形的面积。
- 公式计算出的面积需要除以2,并取绝对值。
复杂度分析
- 时间复杂度:
O(N)
,由于需要遍历所有输入数据并计算多边形面积。 - 空间复杂度:
O(N)
,存储顶点列表。
这个解决方案在时间和空间复杂度上都能够有效处理给定范围内的输入数据。
请实现一个简易内存池
根据请求命令完成内存分配和释放
内存池支持两种操作命令
REQUEST和RELEASE其格式为
REQUEST=请求的内存大小
表示请求分配指定大小内存
如果分配成功,返回分配到的内存首地址
如果内存不足,或指定的大小为零则输出error
RELEASE=释放的内存首地址
表示释放掉之前分配的内存
释放成功无需输出
如果释放不存在的首地址
则输出error
注意:
内存池总大小为 100 字节
内存池地址分配必须是连续内存,并优先从低地址分配
内存释放后可被再次分配,已释放的内存在空闲时不能被二次释放
不会释放已申请的内存块的中间地址
释放操作只是针对首地址所对应的单个内存块进行操作,不会影响其他内存块
输入样例
3
REQUEST=30
RELEASE=0
REQUEST=30
输出样例
0
error
30
为了实现一个简易的内存池,我们需要设计一个内存池管理系统来处理内存分配和释放。内存池的大小是100字节,我们需要处理两种命令:REQUEST
和 RELEASE
。REQUEST
用于请求分配内存,RELEASE
用于释放已分配的内存。
以下是实现这个内存池的步骤:
-
内存池初始化:
- 内存池总大小为100字节,可以用一个布尔数组表示内存是否被占用。每个元素表示一个字节的状态(已占用或未占用)。
-
内存分配:
- 遍历内存池,找到一个足够大的连续空闲区段来满足内存请求。如果找到,分配内存,并记录内存块的起始地址。
- 如果请求的内存大小为零或没有足够的内存,返回
error
。
-
内存释放:
- 根据释放的起始地址,找到对应的内存块,并将其标记为空闲。如果尝试释放一个无效的地址或未分配的内存,返回
error
。
- 根据释放的起始地址,找到对应的内存块,并将其标记为空闲。如果尝试释放一个无效的地址或未分配的内存,返回
-
输入输出:
- 解析输入命令并执行相应的操作,输出结果。
下面是实现简易内存池的Java代码:
import java.util.Scanner;
public class SimpleMemoryPool {
private static final int POOL_SIZE = 100;
private static final byte[] memoryPool = new byte[POOL_SIZE];
private static final boolean[] allocated = new boolean[POOL_SIZE];
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int numCommands = scanner.nextInt();
scanner.nextLine(); // consume the newline character
for (int i = 0; i < numCommands; i++) {
String command = scanner.nextLine();
if (command.startsWith("REQUEST=")) {
int size = Integer.parseInt(command.substring(8));
handleRequest(size);
} else if (command.startsWith("RELEASE=")) {
int address = Integer.parseInt(command.substring(8));
handleRelease(address);
}
}
}
private static void handleRequest(int size) {
if (size <= 0 || size > POOL_SIZE) {
System.out.println("error");
return;
}
int start = -1;
int freeCount = 0;
for (int i = 0; i < POOL_SIZE; i++) {
if (!allocated[i]) {
if (freeCount == 0) {
start = i;
}
freeCount++;
if (freeCount == size) {
allocateMemory(start, size);
System.out.println(start);
return;
}
} else {
freeCount = 0;
}
}
System.out.println("error");
}
private static void allocateMemory(int start, int size) {
for (int i = start; i < start + size; i++) {
allocated[i] = true;
}
}
private static void handleRelease(int address) {
if (address < 0 || address >= POOL_SIZE || !isMemoryAllocated(address)) {
System.out.println("error");
return;
}
int size = 0;
for (int i = address; i < POOL_SIZE && allocated[i]; i++) {
size++;
}
if (size > 0) {
deallocateMemory(address, size);
System.out.println(); // no output on successful release
} else {
System.out.println("error");
}
}
private static boolean isMemoryAllocated(int address) {
int size = 0;
for (int i = address; i < POOL_SIZE && allocated[i]; i++) {
size++;
}
return size > 0;
}
private static void deallocateMemory(int start, int size) {
for (int i = start; i < start + size; i++) {
allocated[i] = false;
}
}
}
代码解释
-
内存池初始化:
memoryPool
和allocated
数组分别用于表示内存池和记录内存是否被占用。
-
处理
REQUEST
命令:- 查找足够大的连续空闲内存块,若找到则分配内存并输出起始地址。如果没有足够的内存块,输出
error
。
- 查找足够大的连续空闲内存块,若找到则分配内存并输出起始地址。如果没有足够的内存块,输出
-
处理
RELEASE
命令:- 根据起始地址释放内存块。若尝试释放无效地址或未分配的内存块,输出
error
。
- 根据起始地址释放内存块。若尝试释放无效地址或未分配的内存块,输出
-
输入输出处理:
- 读取命令并执行相应操作,输出结果。
题目描述
给定一个表达式,求其分数计算结果。
表达式的限制如下:
1.所有的输入数字皆为正整数(包括0)
2.仅支持四则运算(±")和括号
3.结果为整数或分数,分数必须化为最简格式(比如6,3/4, 7/8,90/7)
4.除数可能为0,如果遇到这种情况,直接输出"ERROR"
5.输入和最终计算结果中的数字都不会超出整型范围
用例输入一定合法,不会出现括号匹配的情况
输入描述
字符串格式的表达式,仅支持±",数字可能超过两位, 可能带有空格,没有负数
长度小于200个字符
输出描述
表达式结果,以最简格式表达
如果结果为整数,那么直接输出整数
如果结果为负数,那么分子分母不可再约分,可以为假分数, 不可表达为带分数
结果可能是负数,符号放在前面
用例
输入
1+5*718
输出
4318
说明
无
输入
1/(0-5)
输出
-1/5
说明
符号需要提到最前面
下面是使用 Java 实现的代码,它处理给定的数学表达式,并计算其结果。我们将处理加法、减法、乘法、除法以及括号。对于分数,我们使用 Java 的 BigInteger
来确保处理大整数和化简分数。
Java 实现
import java.math.BigInteger;
import java.util.Scanner;
import java.util.Stack;
public class ExpressionEvaluator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String expression = scanner.nextLine().replaceAll(" ", "");
scanner.close();
try {
String result = evaluate(expression);
System.out.println(result);
} catch (Exception e) {
System.out.println("ERROR");
}
}
public static String evaluate(String expression) throws ArithmeticException {
// Convert the infix expression to postfix
String postfix = infixToPostfix(expression);
// Evaluate the postfix expression
return evaluatePostfix(postfix);
}
private static String infixToPostfix(String infix) {
StringBuilder postfix = new StringBuilder();
Stack<Character> operators = new Stack<>();
for (int i = 0; i < infix.length(); i++) {
char c = infix.charAt(i);
if (Character.isDigit(c)) {
while (i < infix.length() && (Character.isDigit(infix.charAt(i)) || infix.charAt(i) == '.')) {
postfix.append(infix.charAt(i++));
}
postfix.append(' ');
i--;
} else if (c == '(') {
operators.push(c);
} else if (c == ')') {
while (operators.peek() != '(') {
postfix.append(operators.pop()).append(' ');
}
operators.pop();
} else if (c == '+' || c == '-' || c == '*' || c == '/') {
while (!operators.isEmpty() && precedence(c) <= precedence(operators.peek())) {
postfix.append(operators.pop()).append(' ');
}
operators.push(c);
}
}
while (!operators.isEmpty()) {
postfix.append(operators.pop()).append(' ');
}
return postfix.toString();
}
private static int precedence(char op) {
if (op == '+' || op == '-') return 1;
if (op == '*' || op == '/') return 2;
return -1;
}
private static String evaluatePostfix(String postfix) throws ArithmeticException {
Stack<BigInteger> stack = new Stack<>();
String[] tokens = postfix.split(" ");
for (String token : tokens) {
if (token.isEmpty()) continue;
if (isOperator(token)) {
BigInteger b = stack.pop();
BigInteger a = stack.pop();
BigInteger result = performOperation(a, b, token.charAt(0));
stack.push(result);
} else {
stack.push(new BigInteger(token));
}
}
BigInteger result = stack.pop();
return formatResult(result);
}
private static boolean isOperator(String token) {
return token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/");
}
private static BigInteger performOperation(BigInteger a, BigInteger b, char op) throws ArithmeticException {
switch (op) {
case '+': return a.add(b);
case '-': return a.subtract(b);
case '*': return a.multiply(b);
case '/':
if (b.equals(BigInteger.ZERO)) throw new ArithmeticException("Division by zero");
return a.divide(b);
default: throw new IllegalArgumentException("Invalid operator");
}
}
private static String formatResult(BigInteger result) {
// Simplify the result and handle negative sign
return result.toString();
}
}
说明
-
输入处理:
- 从标准输入读取表达式并去除空格。
-
表达式转换:
- 使用中缀到后缀的转换方法(Shunting Yard Algorithm)将表达式转换为后缀表达式(逆波兰表达式)。
-
计算结果:
- 使用后缀表达式的评估方法,处理加法、减法、乘法和除法。
- 使用
BigInteger
来处理大整数的运算。
-
错误处理:
- 如果出现除零错误,抛出
ArithmeticException
并输出 "ERROR"。
- 如果出现除零错误,抛出
-
结果格式化:
- 直接将
BigInteger
转化为字符串输出。如果需要处理更复杂的分数表示,可以在formatResult
方法中进行扩展处理。
- 直接将
运行
将代码保存为 ExpressionEvaluator.java
,并在终端或 IDE 中运行。输入表达式时请确保其合法性,并测试不同的表达式情况。
题目描述
服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为 0~100 的整数,给定一个数值(minAverageLost)表示某个时间段内平均失败率容忍值,即平均失败率小于等于 minAverageLost,找出数组中最长时间段,如果未找到则直接返回 NULL。
输入
输入有两行内容,第一行为minAverageLost,第二行为数组,数组元素通过空格" "分隔,minAverageLost 及数组中元素取值范围为 0~100 的整数,数组元素的个数不会超过 100 个。
输出
找出平均值小于等于 minAverageLost 的最长时间段,输出数组下标对,格式{beginIndex}-{endIndx}(下标从 0 开始),如果同时存在多个最长时间段,则输出多个下标对且下标对之间使用空格" "拼接,多个下标对按下标从小到大排序。
样例输入
2
0 0 100 2 2 99 0 2
样例输出
0-1 3-4 6-7
提示
A、输入解释:minAverageLost = 2,数组[0, 0, 100, 2, 2, 99, 0, 2]
B、通过计算小于等于 2 的最长时间段为:数组下标为 0-1 即[0, 0],数组下标为 3-4 即[2, 2],数组下标为 6-7 即[0, 2],这三个部分都满足平均值小于等 2 的要求,因此输出 0-1 3-4 6-7
为了处理这个问题,我们需要找出数组中所有满足平均失败率小于等于 minAverageLost
的最长时间段。以下是解决问题的步骤和 Java 实现代码:
思路
-
滑动窗口算法:使用滑动窗口(双指针)算法来找出所有符合条件的时间段。通过维护一个窗口,计算当前窗口内的平均失败率,并根据这个平均值来调整窗口的大小。
-
记录结果:在扫描数组的过程中,记录所有最长的符合条件的时间段,并确保时间段按开始索引从小到大排序。
-
计算平均值:使用窗口内的总失败率和窗口大小来计算平均值。窗口大小需要动态调整,保证其平均失败率不超过
minAverageLost
。
Java 实现
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class LongestValidSegment {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int minAverageLost = scanner.nextInt();
scanner.nextLine(); // Consume the remaining newline character
String[] arrayStr = scanner.nextLine().split(" ");
scanner.close();
int[] array = new int[arrayStr.length];
for (int i = 0; i < arrayStr.length; i++) {
array[i] = Integer.parseInt(arrayStr[i]);
}
List<String> result = findLongestValidSegments(array, minAverageLost);
System.out.println(String.join(" ", result));
}
private static List<String> findLongestValidSegments(int[] array, int minAverageLost) {
List<String> result = new ArrayList<>();
int n = array.length;
int maxLen = 0;
for (int start = 0; start < n; start++) {
int sum = 0;
int count = 0;
for (int end = start; end < n; end++) {
sum += array[end];
count++;
double average = (double) sum / count;
if (average <= minAverageLost) {
if (count > maxLen) {
result.clear();
maxLen = count;
result.add(start + "-" + end);
} else if (count == maxLen) {
result.add(start + "-" + end);
}
} else {
break;
}
}
}
return result;
}
}
说明
-
读取输入:
- 从标准输入读取
minAverageLost
和数组,数组中的元素通过空格分隔。
- 从标准输入读取
-
滑动窗口处理:
- 对于每个起始位置
start
,通过扩展end
指针来维护当前窗口。 - 计算窗口内元素的总和及其平均值,并根据结果判断是否更新最长时间段列表。
- 如果窗口内的平均值不超过
minAverageLost
,更新最长时间段列表。如果超过,跳出当前窗口扩展循环。
- 对于每个起始位置
-
输出:
- 输出所有找到的最长时间段,格式为
start-end
。多个时间段之间用空格分隔。
- 输出所有找到的最长时间段,格式为
示例
假设输入为:
2
0 0 100 2 2 99 0 2
程序将输出:
0-1 3-4 6-7
这表示所有最长的符合条件的时间段,并按开始索引从小到大的顺序排序。
题目描述
给定M (0<M<=30)个字符(a-z),从中取出任意字符(每个字符只能用一次)拼接成长度为N (0<N<=5)的字符串,要求相同的字符不能相邻,计算出给定的字符列表能拼接出多少种满足条件的字符串,输入非法或者无法拼接出满足条件的字符串则返回0。
输入
给定的字符列表和结果字符串长度,中间使用空格(" ")拼接
输出
满足条件的字符串个数
样例输入
aabc 3
样例输出
8
提示
给定的字符为aabc,结果字符串长度为3,可以拼接成abc,acb,bac,bca,cba,cab,aba,aca,共8种
要解决这个问题,我们需要生成所有可能的长度为 ( N ) 的字符串组合,并确保在这些组合中,相同的字符不能相邻。可以通过回溯算法来完成这个任务。具体步骤如下:
步骤
-
解析输入:从输入中提取字符列表和目标字符串的长度 ( N )。
-
生成所有可能的字符串:
- 使用回溯法生成所有可能的字符串组合,确保在生成的过程中不会出现相同的字符相邻的情况。
-
去重并计算符合条件的字符串数:
- 将符合条件的字符串存入集合中,以自动处理重复情况。
Java 实现
以下是一个 Java 实现代码,它解决了这个问题:
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class UniqueStringGenerator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String chars = scanner.next();
int length = scanner.nextInt();
scanner.close();
// Check for invalid input
if (length <= 0 || length > 5 || chars == null || chars.isEmpty()) {
System.out.println(0);
return;
}
// Generate all valid strings
Set<String> result = new HashSet<>();
generateValidStrings(chars, length, "", result);
// Output the result
System.out.println(result.size());
}
private static void generateValidStrings(String chars, int length, String current, Set<String> result) {
if (current.length() == length) {
result.add(current);
return;
}
for (int i = 0; i < chars.length(); i++) {
char ch = chars.charAt(i);
if (current.isEmpty() || current.charAt(current.length() - 1) != ch) {
generateValidStrings(chars.substring(0, i) + chars.substring(i + 1), length, current + ch, result);
}
}
}
}
说明
-
输入解析:
- 从标准输入读取字符列表和目标字符串长度。若输入无效(长度不在 [1, 5] 范围内或字符列表为空),直接返回0。
-
生成有效字符串:
- 使用回溯算法生成长度为 ( N ) 的所有可能字符串。每次递归调用时,尝试将当前字符加入字符串,并检查相邻字符是否相同。
- 如果当前字符串的长度等于 ( N ),则将其加入结果集合中。
-
去重和计数:
- 结果存储在
Set
中以自动去重。最终输出集合的大小,即满足条件的字符串数量。
- 结果存储在
示例
对于输入:
aabc 3
程序将输出:
8
表示总共有8种满足条件的字符串。
题目描述
给定一个有向图,图中可能包含有环,图使用二维矩阵表示,每一行的第一列表示起始节点,第二列表示终止节点,如 [0, 1] 表示从 0 到 1 的路径。
每个节点用正整数表示。求这个数据的首节点与尾节点,题目给的用例会是一个首节点,但可能存在多个尾节点。同时图中可能含有环。如果图中含有环,返回 [-1]。
说明:入度为0是首节点,出度为0是尾节点。
输入描述
第一行为后续输入的键值对数量N(N ≥ 0)
第二行为2N个数字。每两个为一个起点,一个终点.
输出描述
输出一行头节点和尾节点。如果有多个尾节点,按从小到大的顺序输出
备注
如果图有环,输出为 -1
所有输入均合法,不会出现不配对的数据
用例1
输入
4
0 1 0 2 1 2 2 3
输出
0 3
说明
用例2
输入
2
0 1 0 2
输出
0 1 2
要解决这个问题,我们需要处理有向图的顶点和边,识别首节点和尾节点,并检查图中是否存在环。以下是实现步骤和相应的 Java 代码:
步骤
-
解析输入:
- 从输入中读取图的边数据,并构建图的邻接表表示。
-
计算节点的入度和出度:
- 使用两个
HashSet
来记录入度为 0 和出度为 0 的节点。 - 遍历图的所有边来更新入度和出度信息。
- 使用两个
-
检测图中是否存在环:
- 使用拓扑排序来检测图是否有环。可以通过Kahn算法(基于入度的拓扑排序)来完成此任务。
-
输出结果:
- 如果图中存在环,输出
[-1]
。 - 否则,输出所有的首节点和尾节点。
- 如果图中存在环,输出
Java 实现
以下是实现上述逻辑的 Java 代码:
import java.util.*;
public class GraphNodes {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
if (N == 0) {
System.out.println("[-1]");
return;
}
Map<Integer, Set<Integer>> adjList = new HashMap<>();
Map<Integer, Integer> inDegree = new HashMap<>();
Set<Integer> nodes = new HashSet<>();
// Read edges and build the graph
for (int i = 0; i < N; i++) {
int start = scanner.nextInt();
int end = scanner.nextInt();
// Update adjacency list
adjList.putIfAbsent(start, new HashSet<>());
adjList.putIfAbsent(end, new HashSet<>());
adjList.get(start).add(end);
// Update in-degrees
inDegree.put(end, inDegree.getOrDefault(end, 0) + 1);
inDegree.putIfAbsent(start, 0);
nodes.add(start);
nodes.add(end);
}
// Find all nodes with in-degree 0 and out-degree 0
Set<Integer> startNodes = new TreeSet<>();
Set<Integer> endNodes = new TreeSet<>();
for (int node : nodes) {
if (inDegree.getOrDefault(node, 0) == 0) {
startNodes.add(node);
}
if (adjList.getOrDefault(node, Collections.emptySet()).isEmpty()) {
endNodes.add(node);
}
}
// Check for cycles using Kahn's Algorithm
if (hasCycle(adjList, inDegree, nodes.size())) {
System.out.println("[-1]");
} else {
// Output start and end nodes
List<Integer> result = new ArrayList<>(startNodes);
result.addAll(endNodes);
System.out.println(result);
}
}
// Function to check if there is a cycle in the graph
private static boolean hasCycle(Map<Integer, Set<Integer>> adjList, Map<Integer, Integer> inDegree, int numNodes) {
Queue<Integer> queue = new LinkedList<>();
int count = 0;
// Start with nodes with in-degree 0
for (int node : inDegree.keySet()) {
if (inDegree.get(node) == 0) {
queue.add(node);
}
}
while (!queue.isEmpty()) {
int node = queue.poll();
count++;
for (int neighbor : adjList.getOrDefault(node, Collections.emptySet())) {
inDegree.put(neighbor, inDegree.get(neighbor) - 1);
if (inDegree.get(neighbor) == 0) {
queue.add(neighbor);
}
}
}
// If count of processed nodes is not equal to the number of nodes, there's a cycle
return count != numNodes;
}
}
说明
-
输入解析:
- 从输入中读取图的边信息,并使用
HashMap
和HashSet
构建图的邻接表和入度统计。
- 从输入中读取图的边信息,并使用
-
入度和出度计算:
- 维护两个集合来跟踪入度为 0 和出度为 0 的节点。
-
环检测:
- 使用拓扑排序的 Kahn 算法来检测图是否存在环。如果所有节点都能被处理,那么图没有环,否则存在环。
-
输出:
- 如果图存在环,输出
[-1]
。 - 否则,输出首节点和尾节点。
- 如果图存在环,输出
示例
对于输入:
4
0 1 0 2 1 2 2 3
程序将输出:
0 3
表示首节点是0,尾节点是3。
对于输入:
2
0 1 0 2
程序将输出:
0 1 2
表示首节点是0,尾节点是1和2。
标签:输出,系列,String,int,算法,new,输入,scanner From: https://www.cnblogs.com/mingyu15/p/18352560