代码随想录算法训练营第59天|卡码网 47. 参加科学大会、94. 城市间货物运输 I

1. 卡码网 47. 参加科学大会

题目链接:https://kamacoder.com/problempage.php?pid=1047
文章链接:https://www.programmercarl.com/kamacoder/0047.参会dijkstra堆.html#总结
在这里插入图片描述

思路依然是 dijkstra 三部曲:
1.第一步,选源点到哪个节点近且该节点未被访问过
2.第二步,该最近节点被标记访问过
3.第三步,更新非访问节点到源点的距离(即更新minDist数组)
只不过之前是 通过遍历节点来遍历边,通过两层for循环来寻找距离源点最近节点。 这次我们直接遍历边,且通过堆来对边进行排序,达到直接选择距离源点最近节点。
优化部分
1.针对稀疏图,从边的角度使用邻接表进行图存储。
2.选源点到哪个节点近且该节点未被访问过。
我们要选择距离源点近的节点(即:该边的权值最小),所以 我们需要一个 小顶堆 来帮我们对边的权值排序,每次从小顶堆堆顶 取边就是权值最小的边。注意:这里的权值是节点到源点的权值,小顶堆需要按照该权值来排序。
小顶堆可以使用优先级队列实现。最小堆中保存着所有未被访问的且权值不是默认最大值的节点。
3.该最近节点被标记访问过
和 朴素dijkstra 一样。
4.更新非访问节点到源点的距离(即更新minDist数组)
和 朴素dijkstra 一样。唯一的区别是因为使用的是邻接表,则可以直接获取到当前最近节点的所有直连节点,而不用同邻接矩阵一样,需要遍历所有节点再判断。

class Edge {
    int to; // 邻接顶点
    int val; // 边的权值
    Edge(int to,int val) {
        this.to = to;
        this.val = val;
    }
}

class MyComparison implements Comparator<Pair<Integer,Integer>> {
    @Override
    public int compare(Pair<Integer,Integer> a,Pair<Integer,Integer> b) {
        return Integer.compare(a.second,b.second); // 权值排序
    }
}

class Pair<U,V> {
    public final U first; // 节点
    public final V second; // 节点到源点的最小权值
    public Pair(U first,V second) {
        this.first = first;
        this.second = second;
    }
}

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<Edge>> grid = new ArrayList<>(n+1);
        for (int i=0;i<=n;i++) {
            grid.add(new ArrayList<>());
        }
        
        for (int i=0;i<m;i++) {
            int p1 = scanner.nextInt();
            int p2 = scanner.nextInt();
            int val = scanner.nextInt();
            grid.get(p1).add(new Edge(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];
        
        // 优先队列中存放Pair<节点,源点到该节点的权值>
        PriorityQueue<Pair<Integer, Integer>> pd = new PriorityQueue<>(new MyComparison());
        
        // 初始化队列,源点到源点的距离为0,所以初始为0
        pd.add(new Pair<>(start,0));
        
        minDist[start] = 0; // 起始点到自身的距离为0
        
        while (!pd.isEmpty()) {
            // 1. 第一步 选源点到哪个节点近且该节点未被访问过
            Pair<Integer,Integer> cur = pd.poll();
            if (visited[cur.first]) continue; // 注意:因为最小堆中会存在多个不同权值的相同节点。当该节点的最小权值节点被取出后,
            //节点会被标记为被访问true。那么该节点的其他权值节点就无用了,直接跳过。
            
            // 2. 第二步,该最近节点被标记访问过
            visited[cur.first] = true;
            
            // 3. 第三步,更新非访问节点到源点的距离
            for (Edge edge:grid.get(cur.first)) {
                if (!visited[edge.to] && minDist[cur.first] + edge.val < minDist[edge.to]) {
                    minDist[edge.to] = minDist[cur.first] + edge.val;
                    pd.add(new Pair<>(edge.to,minDist[edge.to])); // 将更新后的距源点的权值的节点加入到最小堆中
                    // 注意:这里并没有删除原来权值的相同节点,而是将新的最小权值作为新的节点加入了最小堆中。
                    // 也就是说最小堆中会存在多个不同权值的相同节点。
                    
                }
            }
         }
         
         if (minDist[end] == Integer.MAX_VALUE) {
             System.out.println(-1);
         } else {
             System.out.println(minDist[end]);
         }
         
        
    }
}

2. 卡码网 94. 城市间货物运输 I

题目链接:https://kamacoder.com/problempage.php?pid=1152
文章链接:https://www.programmercarl.com/kamacoder/0094.城市间货物运输I.html

在这里插入图片描述

思路:
使用Bellman_ford算法
核心思想是 对所有边进行松弛n-1次操作(n为节点数量),从而求得目标最短路。
针对:图中边的权值可以有负数,且不存在任何负权回路的情况(在有向图中出现有向环 且环的总权值为负数)。
对所有边松弛一次,可以计算 起点到达 与起点一条边相连的节点 的最短距离。
对所有边松弛两次,可以计算 起点到达 与起点两条边相连的节点 的最短距离。
对所有边松弛三次,可以计算 起点到达 与起点三条边相连的节点 的最短距离。
若计算起点到终点的最短距离,则需要对边松弛n-1次,其中n为节点数。

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(); // 道路数量
        
        List<List<Integer>> roadList = new ArrayList<>();
        for (int i=0;i<m;i++) {
            int s = sc.nextInt();
            int t = sc.nextInt();
            int v = sc.nextInt();
            List<Integer> list = new ArrayList<>();
            list.add(s);
            list.add(t);
            list.add(v);
            roadList.add(list);
        }
        
        int[] minDist = new int[n+1]; // 记录起点到各个节点的最短距离
        Arrays.fill(minDist,Integer.MAX_VALUE);
        minDist[1]=0;
        
        // 松弛n-1次
        for(int j=1;j<n;j++) {
            // 对所有道路进行分析
            for(int i=0;i<m;i++) {
                int from = roadList.get(i).get(0);
                int to = roadList.get(i).get(1);
                int value = roadList.get(i).get(2);
                if (minDist[from] != Integer.MAX_VALUE && minDist[from] + value < minDist[to]) {
                    minDist[to] = minDist[from] + value;
                }
            }
        }
        
        if (minDist[n] == Integer.MAX_VALUE) {
            System.out.println("unconnected");
        } else {
            System.out.println(minDist[n]);
        }
        
    }
}
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值