Day51 | 117. 软件构建(拓扑排序)47. 参加科学大会 dijkstra(朴素版)

语言

117. 软件构建

117. 软件构建

题目

题目描述

某个大型软件项目的构建系统拥有 N 个文件,文件编号从 0 到 N - 1,在这些文件中,某些文件依赖于其他文件的内容,这意味着如果文件 A 依赖于文件 B,则必须在处理文件 A 之前处理文件 B (0 <= A, B <= N - 1)。请编写一个算法,用于确定文件处理的顺序。

输入描述

第一行输入两个正整数 N, M。表示 N 个文件之间拥有 M 条依赖关系。

后续 M 行,每行两个正整数 S 和 T,表示 T 文件依赖于 S 文件。

输出描述

输出共一行,如果能处理成功,则输出文件顺序,用空格隔开。

如果不能成功处理(相互依赖),则输出 -1。

思路

  1. 初始化
    • 创建一个邻接表 umap 来存储图结构,即文件之间的依赖关系。
    • 创建一个数组 inDegree 来记录每个文件的入度,即有多少个其他文件依赖于它。
  2. 读取输入
    • 从标准输入读取两个整数 nm,分别代表文件的数量和依赖关系的数量。
    • 接着读取 m 行,每行包含两个整数 st,表示文件 s 依赖于文件 t
  3. 构建图
    • 根据输入更新 umapinDegree
  4. 拓扑排序
    • 将所有入度为 0 的文件加入到队列 queue 中。
    • 循环直到队列为空,每次从队列中取出一个文件 cur 并将其添加到结果列表 result 中。
    • 更新 cur 文件所指向的所有文件的入度,并将入度变为 0 的文件加入队列。
  5. 输出结果
    • 如果所有文件都被成功排序,则输出它们的顺序;否则,如果存在循环依赖(即不是所有的文件都能被处理),则输出 -1

代码

java 复制代码
import java.util.*;


public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        List<List<Integer>> umap = new ArrayList<>(); // 记录文件依赖关系
        int[] inDegree = new int[n]; // 记录每个文件的入度

        for (int i = 0; i < n; i++)
            umap.add(new ArrayList<>());

        for (int i = 0; i < m; i++) {
            int s = scanner.nextInt();
            int t = scanner.nextInt();
            umap.get(s).add(t); // 记录s指向哪些文件
            inDegree[t]++; // t的入度加一
        }

        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) {
                // 入度为0的文件,可以作为开头,先加入队列
                queue.add(i);
            }
        }

        List<Integer> result = new ArrayList<>();

        // 拓扑排序
        while (!queue.isEmpty()) {
            int cur = queue.poll(); // 当前选中的文件
            result.add(cur);
            for (int file : umap.get(cur)) {
                inDegree[file]--; // cur的指向的文件入度-1
                if (inDegree[file] == 0) {
                    queue.add(file);
                }
            }
        }

        if (result.size() == n) {
            for (int i = 0; i < result.size(); i++) {
                System.out.print(result.get(i));
                if (i < result.size() - 1) {
                    System.out.print(" ");
                }
            }
        } else {
            System.out.println(-1);
        }
    }
}

易错点

只有一个文件 (n=1) 或者没有文件 (n=0) 的情况。

47. 参加科学大会

47. 参加科学大会(第六期模拟笔试)

题目

题目描述

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。

小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。

小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。

输入描述

第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。

接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。

输出描述

输出一个整数,代表小明从起点到终点所花费的最小时间。

思路

  1. 初始化

    • 创建一个二维数组 grid 来存储图结构,其中 grid[i][j] 表示从节点 i 到节点 j 的边的权重。如果两节点之间没有直接连接,则用 Integer.MAX_VALUE 表示无穷大。
    • 初始化 minDist 数组,用于记录从源点到各个节点的最短距离。所有节点的初始距离都设置为无穷大,除了起始点自身设为 0。
    • 初始化 visited 数组,用于记录每个节点是否已经被访问过。
  2. 读取输入

    • 从标准输入读取两个整数 nm,分别代表顶点的数量和边的数量。
    • 接着读取 m 行,每行包含三个整数 p1, p2, val,表示从节点 p1 到节点 p2 有一条边,其权重为 val
  3. Dijkstra 算法

    • 选择距离源点最近且未被访问过的节点 cur
    • 将此节点标记为已访问。
    • 更新所有未访问节点到源点的距离,如果通过 cur 可以到达更近的距离,则更新距离。
  4. 输出结果

    • 如果 minDist[end] 仍然是 Integer.MAX_VALUE,说明不存在从起点到终点的路径,输出 -1
    • 否则输出从起点到终点的最短路径长度。

代码

java 复制代码
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        int[][] grid = new int[n + 1][n + 1];
        for (int i = 0; i <= n; i++) {
            Arrays.fill(grid[i], Integer.MAX_VALUE);
        }

        for (int i = 0; i < m; i++) {
            int p1 = scanner.nextInt();
            int p2 = scanner.nextInt();
            int val = scanner.nextInt();
            grid[p1][p2] = val;
        }

        int start = 1;
        int end = n;

        // 存储从源点到每个节点的最短距离
        int[] minDist = new int[n + 1];
        Arrays.fill(minDist, Integer.MAX_VALUE);

        // 记录顶点是否被访问过
        boolean[] visited = new boolean[n + 1];

        minDist[start] = 0;  // 起始点到自身的距离为0

        for (int i = 1; i <= n; i++) { // 遍历所有节点

            int minVal = Integer.MAX_VALUE;
            int cur = 1;

            // 1、选距离源点最近且未访问过的节点
            for (int v = 1; v <= n; ++v) {
                if (!visited[v] && minDist[v] < minVal) {
                    minVal = minDist[v];
                    cur = v;
                }
            }

            visited[cur] = true;  // 2、标记该节点已被访问

            // 3、第三步,更新非访问节点到源点的距离(即更新minDist数组)
            for (int v = 1; v <= n; v++) {
                if (!visited[v] && grid[cur][v] != Integer.MAX_VALUE && minDist[cur] + grid[cur][v] < minDist[v]) {
                    minDist[v] = minDist[cur] + grid[cur][v];
                }
            }
        }

        if (minDist[end] == Integer.MAX_VALUE) {
            System.out.println(-1); // 不能到达终点
        } else {
            System.out.println(minDist[end]); // 到达终点最短路径
        }
    }
}

易错点

如果 n 很小(如 n=1),或者 m=0(没有边的情况),需要特别处理

总结

今天学了图论的两种算法

继续加油!

迟早有一天可以成功!

我一定会坚持下来。

凡事以理想为因,实行为果

相关推荐
45288655上山打老虎几秒前
List容器
数据结构·windows·list
tryxr7 分钟前
HashTable、HashMap、ConcurrentHashMap 之间的区别
java·开发语言·hash
松涛和鸣9 分钟前
DAY32 Linux Thread Programming
linux·运维·数据库·算法·list
无事好时节12 分钟前
Linux 线程
java·开发语言·rpc
LYFlied13 分钟前
【每日算法】LeetCode 234. 回文链表详解
算法·leetcode·链表
我家领养了个白胖胖17 分钟前
Prompt、格式化输出、持久化ChatMemory
java·后端·ai编程
sszdlbw31 分钟前
后端springboot框架入门学习--第二篇
java·spring boot·学习
阿拉斯攀登32 分钟前
MyBatis 全面解析 & Spring Boot 集成实战
java·spring boot·mybatis·持久层框架
A尘埃33 分钟前
Java业务场景(高并发+高可用+分布式)
java·开发语言·分布式
白仑色38 分钟前
java中的anyMatch和allMatch方法
java·linux·windows·anymatch·allmatch