1. 卡码网 117. 软件构建
题目链接:https://kamacoder.com/problempage.php?pid=1191
文章链接:https://www.programmercarl.com/kamacoder/0117.软件构建.html
思路:使用BFS
BFS的实现思路:
拓扑排序的过程,其实就两步:
1.找到入度为0 的节点,加入结果集
2.将该节点从图中移除
循环以上两步,直到 所有节点都在图中被移除了。
结果集的顺序,就是我们想要的拓扑排序顺序 (结果集里顺序可能不唯一)
import java.util.*;
public class Main {
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 文件数量
int m = sc.nextInt(); // 依赖关系
int[] inDegree = new int[n];
Map<Integer,List<Integer>> map = new HashMap<>();
for (int i=0;i<m;i++) {
int s = sc.nextInt();
int t = sc.nextInt();
inDegree[t]++;
List<Integer> list = map.getOrDefault(s,new ArrayList<>());
list.add(t);
map.put(s,list);
}
Deque<Integer> deque = new ArrayDeque<>();
for (int i=0;i<n;i++) {
if (inDegree[i] == 0) {
deque.offer(i); // 注意:使用队列保存入度为0的节点。
}
}
List<Integer> res = new ArrayList<>(); // 结果集
while (!deque.isEmpty()) {
// 1.找到入度为0的节点,并加入结果集
int s = deque.poll();
res.add(s);
List<Integer> files = map.get(s); // 获取所有依赖s的集合
if (files == null) {
continue;
}
for (Integer t:files) {
// 2.将s节点从图中移除
inDegree[t]--;
if (inDegree[t] == 0) {
deque.offer(t);
}
}
}
if (res.size() != n) {
System.out.println(-1);
} else {
for (int i=0;i<res.size()-1;i++) {
System.out.print(res.get(i));
System.out.print(" ");
}
System.out.print(res.get(res.size() - 1));
}
}
}
2. 卡码网 47. 参加科学大会
题目链接:https://kamacoder.com/problempage.php?pid=1047
文章链接:https://www.programmercarl.com/kamacoder/0047.参会dijkstra朴素.html
思路:本题是求最短路径问题。可以使用dijkstra算法。
dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。
dijkstra三部曲:
1.第一步,选源点到哪个节点近且该节点未被访问过
2.第二步,该最近节点被标记访问过
3.第三步,更新非访问节点到源点的距离(即更新minDist数组)
minDist数组 用来记录 每一个节点距离源点的最小距离。这里的源点指的是起始点,即本题中的节点1。
需要注意两点:
- dijkstra 算法可以同时求 起点到所有节点的最短路径
- 权值不能为负数 (注意这里)。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 公共汽车站数量
int m = sc.nextInt(); // 公路数量
int[][] timeArray = new int[n+1][n+1];
for (int i=0;i<n;i++) {
Arrays.fill(timeArray[i],Integer.MAX_VALUE);
}
// 构建邻接矩阵
for (int i=0;i<m;i++) {
int s = sc.nextInt();
int e = sc.nextInt();
int v = sc.nextInt();
timeArray[s][e] = v; // 注意:这里只对[s][e]填值,而最小生成树也会对[e][s]填值
}
// 节点是否被访问到
boolean[] visited = new boolean[n+1];
// 所有节点到源点到最短时间
int[] minTime = new int[n+1];
Arrays.fill(minTime,Integer.MAX_VALUE);
minTime[1] = 0; // 节点1到源点的时间为0
for (int t=1;t<=n;t++) {
// 1. 寻找所有未访问节点中到源点最短时间的节点
int min = Integer.MAX_VALUE;
int cur = 1;
for (int i=1;i<=n;i++) {
if (!visited[i] && minTime[i] < min) {
min = minTime[i];
cur = i;
}
}
// 2. 标记被访问
visited[cur] = true;
// 3. 更新所有未被访问节点到源点的最短时间
// 注意:这里只更新与cur直连的节点 由其判断:timeArray[cur][i] != Integer.MAX_VALUE
for (int i=1;i<=n;i++) {
if (!visited[i] && timeArray[cur][i] != Integer.MAX_VALUE && timeArray[cur][i] + minTime[cur] < minTime[i]) {
minTime[i] = timeArray[cur][i] + minTime[cur];
}
}
}
// 未访问到最终节点
if (minTime[n] == Integer.MAX_VALUE) {
System.out.println(-1);
return;
}
System.out.println(minTime[n]);
}
}