Day58-图论,卡码网117,47

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

  • 输入描述:第一行输入两个正整数 N, M。表示 N 个文件之间拥有 M 条依赖关系。后续 M 行,每行两个正整数 S 和 T,表示 T 文件依赖于 S 文件。

  • 输出描述:输出共一行,如果能处理成功,则输出文件顺序,用空格隔开。 如果不能成功处理(相互依赖),则输出 -1。

  • 输入示例

  • 5 4

  • 0 1

  • 0 2

  • 1 3

  • 2 4

  • 输出示例

  • 0 1 2 3 4


  • 思路
  • 拓扑排序的过程:
    • 1.找到入度为0的节点
    • 2.删掉节点(指向的节点入度-1)
    • 循环以上两步,直到 所有节点都在图中被移除了
/**
1. 输入处理部分:使用readline接口读取输入数据
    创建readline接口来处理标准输入
    获取异步迭代器用于逐行读取输入
    readline函数用于读取下一行输入
2. 初始化部分:解析输入数据并构建依赖关系图
    读取文件数量N和依赖关系数量M
    初始化入度数组inDegrees,记录每个文件的入度
    使用Mapumap记录每个文件指向的其他文件(邻接表)
3. 拓扑排序部分:使用Kahn算法实现拓扑排序
    使用队列实现Kahn算法
    初始将所有入度为0的文件加入队列
    每次从队列取出文件,加入结果集
    将该文件指向的所有文件的入度减1,如果减到0则加入队列
4. 结果输出部分:检查并输出排序结果
    检查结果长度是否等于文件总数
        如果相等:输出拓扑排序结果
        如果不相等:说明图中存在环,输出-1
 */
// 创建readline接口 来处理标准输入
const r1 = require('readline').createInterface({ input: process.stdin });
// 创建异步迭代器 用于逐行读取输入
let iter = r1[Symbol.asyncIterator]();
// readline函数 用于读取下一行输入
const readline = async () => (await iter.next()).value;


let N, M // 节点数和边数,读取文件数量N和依赖关系数量M
let inDegrees = [] // 初始化入度数组inDegrees,记录每个文件的入度
let umap = new Map() // 记录文件依赖关系,使用Map umap记录每个文件指向的其他文件(邻接表)
let result = [] // 结果


// 根据输入, 初始化数据
const init = async () => {
  // 读取第一行输入
  let line = await readline();
  // 读取节点数和边数
  [N, M] = line.split(' ').map(Number)

  inDegrees = new Array(N).fill(0)

  // 读取边集
  while (M--) {
    line = await readline();
    // 读取依赖关系x→y
    let [x, y] = line.split(' ').map(Number)

    // 记录入度,y的入度加1
    inDegrees[y]++

    // 记录x指向哪些文件
    if (!umap.has(x)) {
      umap.set(x, [y])
    } else {
      umap.get(x).push(y)
    }
  }
}

(async function () {
  // 根据输入, 初始化数据
  await init()

  let queue = [] // 入度为0的节点
  for (let i = 0; i < N; i++) {
    // 初始时将所有入度为0的文件加入队列
    if (inDegrees[i] == 0) {
      queue.push(i)
    }
  }

  while (queue.length) {
    let cur = queue.shift() //当前文件
    // 加入结果
    result.push(cur)

    let files = umap.get(cur) // 当前文件指向的文件

    // 当前文件指向的文件入度减1
    if (files && files.length) {
      for (let i = 0; i < files.length; i++) {
        // 依赖文件的入度减1
        inDegrees[files[i]]--
        // 如果入度为0则加入队列
        if (inDegrees[files[i]] == 0) queue.push(files[i])
      }
    }
  }

  // 这里result.length == N 一定要判断, 因为可能存在环
  if (result.length == N) return console.log(result.join(' '))
  console.log(-1)
})()


  1. 参加科学大会(第六期模拟笔试)
  • 题目描述:小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。

  • 输入描述:第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。 接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。

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

  • 输入示例

  • 7 9

  • 1 2 1

  • 1 3 4

  • 2 3 2

  • 2 4 5

  • 3 4 2

  • 4 5 3

  • 2 6 4

  • 5 7 4

  • 6 7 9

  • 输出示例

  • 12


  • 思路
  • dijkstra三部曲:
    • 第一步,选源点到哪个节点近且该节点未被访问过,如 源点距离源点最近,距离为0,且未被访问。
    • 第二步,该最近节点被标记访问过,如 标记源点访问过
    • 第三步,更新非访问节点到源点的距离(即更新minDist数组,minDist数组 用来记录 每一个节点距离源点的最小距离。)因为节点2、3和节点1有连接,所以先计算节点2、3到源点到最小距离
    • 依次循环以上步骤,直到找到最短路径或者找不到。
/**
 * 1. 初始化数据结构
    visited: 记录顶点是否已被访问过
    minDist: 记录从起点到各顶点的最短距离,初始值为无穷大(Number.MAX_VALUE)
    将起点到自身的距离设为0
 * 2. Dijkstra算法核心
    每次循环找到一个距离起点最近的未访问顶点
    标记该顶点为已访问
    通过该顶点更新其邻接顶点的最短距离(松弛操作)
 * 3. 返回结果
    如果终点不可达(距离仍为无穷大),返回-1
    否则返回到终点的最短距离
 */
function dijkstra(grid, start, end) {
    // visited: 记录顶点是否已被访问过
    const visited = Array.from({length: end + 1}, () => false)
    // minDist: 记录从起点到各顶点的最短距离,初始值为无穷大(Number.MAX_VALUE)
    const minDist = Array.from({length: end + 1}, () => Number.MAX_VALUE)
    //  将起点到自身的距离设为0
    minDist[start] = 0
    
    for (let i = 1 ; i < end + 1 ; i++) {
        let cur = -1
        let tempMinDist = Number.MAX_VALUE
        // 1. 找寻起始节点距离最近未被访问的节点 
        for (let j = 1 ; j < end + 1 ; j++) {
            if (!visited[j] && minDist[j] < tempMinDist) {
                cur = j
                tempMinDist = minDist[j]
            }
        }
         // 所有可达顶点都已处理
        if (cur === -1) break;
        
        // 2. 更新节点状态为已访问 
        visited[cur] = true
        
        // 3. 更小未被访问节点与起始节点的最短距离 
        for (let j = 1 ; j < end + 1 ; j++) {
            if(!visited[j] && grid[cur][j] != Number.MAX_VALUE 
                && grid[cur][j] + minDist[cur] < minDist[j]
            ) {
                minDist[j] = grid[cur][j] + minDist[cur]
            }
        }
    }
    
    return minDist[end] === Number.MAX_VALUE ? -1 : minDist[end]
}


async function main() {
    // 读取输入
    const rl = require('readline').createInterface({ input: process.stdin })
    const iter = rl[Symbol.asyncIterator]()
    const readline = async () => (await iter.next()).value
    const [n, m] = (await readline()).split(" ").map(Number)
    const grid = Array.from({length: n + 1}, 
        () => Array.from({length:n + 1}, () => Number.MAX_VALUE))
    
    // 构建邻接矩阵
    for (let i = 0 ; i < m ; i++) {
        const [s, e, w] = (await readline()).split(" ").map(Number)
        grid[s][e] = w
    }
    
     // 调用Dijkstra算法
    const result = dijkstra(grid, 1, n)
    
    // 输出结果
    console.log(result)
}


main()



参考&感谢各路大神

posted @ 2025-07-24 10:31  安静的嘶吼  阅读(4)  评论(0)    收藏  举报