首页 > 其他分享 >图论

图论

时间:2024-06-04 21:23:02浏览次数:12  
标签:图论 scanner int static 模块 new 节点

1 图论

1.1 图的建立

1.1.1 领接表边权建图

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    // 定义图的邻接表表示
    static List<int[]>[] g;
    // 节点数
    static int n;
    // 保存某种状态或结果的数组
    static int[] f;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取节点数
        n = scanner.nextInt();
        
        // 初始化图的邻接表
        g = new ArrayList[n + 1];
        for (int i = 1; i <= n; i++) {
            g[i] = new ArrayList<>();
        }
        
        // 初始化状态数组
        f = new int[n + 1];

        // 读取边的信息并构建图
        for (int i = 1; i < n; i++) {
            int a = scanner.nextInt(); // 边的一个端点
            int b = scanner.nextInt(); // 边的另一个端点
            int c = scanner.nextInt(); // 边的权重
            // 添加无向边到邻接表
            g[a].add(new int[]{b, c});
            g[b].add(new int[]{a, c});
        }
    }

    // 深度优先搜索(DFS)遍历图
    static void dfs(int u, int fa) {
        // 遍历节点u的所有邻接节点
        for (int[] pair : g[u]) {
            int x = pair[0]; // 邻接节点
            int w = pair[1]; // 边的权重
            // 如果邻接节点是父节点,则跳过(避免走回头路)
            if (x == fa) continue;
            // 递归遍历邻接节点
            dfs(x, u);
            // 这里可以添加其他的逻辑处理代码
        }
    }
}

  

1.1.2 领接表点权建图

import java.util.Scanner;
import java.util.ArrayList;

public class Main {
    // 定义常量N为节点数的上限
    static final int N = 100010;
    // 用于存储图的邻接表表示
    static ArrayList<Integer>[] g = new ArrayList[N];
    // 用于存储节点的权值
    static int[] w = new int[N];

    // 深度优先搜索(DFS)遍历图
    static void dfs(int u, int fa) {
        // 处理节点u的操作
        // 遍历节点u的所有邻接节点
        for (int x : g[u]) {
            // 如果邻接节点是父节点,则跳过(避免走回头路)
            if (x == fa)
                continue;
            // 递归遍历邻接节点
            dfs(x, u);
            // 处理子节点返回后的操作
        }
    }

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

        // 读取节点数n和边数m
        n = scanner.nextInt();
        m = scanner.nextInt();

        // 初始化邻接表
        for (int i = 1; i <= n; i++) {
            g[i] = new ArrayList<>();
        }

        // 读取边的信息并构建图
        for (int i = 0; i < m; i++) {
            int a, b;
            a = scanner.nextInt();
            b = scanner.nextInt();
            g[a].add(b);  // 添加一条从a到b的边
        }

        // 读取每个节点的权值
        for (int i = 1; i <= n; i++) {
            w[i] = scanner.nextInt();
        }

        // 可以在这里调用dfs进行深度优先搜索,例如从节点1开始
        // dfs(1, -1);
    }
}

  

1.1.3 离散化建图

  1. 点的范围很大(例如 [−109,109]),或者含有负数的点。
  2. 点不是数值,而是字符串,字符串与字符串之间存在相互转换的关系。
import java.util.*;

public class Main {
    public static void main(String[] args) {
        // 定义一个常量N,表示可能的最大节点数
        int N = 100010;
        
        // 定义一个哈希表,用于存储节点之间的边及其权重
        // 键是节点,值是一个包含目标节点及边权的列表
        Map<Integer, List<Map.Entry<Integer, Integer>>> path = new HashMap<>();
        
        // 示例:添加从节点u到节点v,边的权重为w
        int u = 1, v = 2, w = 3;
        
        // 如果哈希表中不包含键u,初始化其值为一个新的ArrayList
        if (!path.containsKey(u)) {
            path.put(u, new ArrayList<>());
        }
        
        // 将边(v, w)添加到节点u的列表中
        path.get(u).add(new AbstractMap.SimpleEntry<>(v, w));
        
        // 示例:遍历节点node的所有相邻节点和边权
        int node = 1;
        
        // 如果哈希表中包含键node,进行遍历
        if (path.containsKey(node)) {
            for (Map.Entry<Integer, Integer> entry : path.get(node)) {
                int target = entry.getKey();  // 相邻节点
                int weight = entry.getValue(); // 边的权重
                // 进行处理,例如打印或其他逻辑
                System.out.println("从节点 " + node + " 到节点 " + target + " 的边权是 " + weight);
            }
        }
    }
}

  

1.2 拓扑排序

 用于判断有向图中是否存在环

LeetCode 207. 课程表

拓扑排序模板题 根据依赖关系建图:先学习课程a必须要先学习课程b,则有b->a这一条有向边。如果可以完成所有课程,则说明存在拓扑排序。  
import java.util.*;

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        // 创建图的邻接表表示
        List<List<Integer>> g = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            g.add(new ArrayList<>()); // 为每个节点初始化邻接表
        }
        
        // 创建数组存储每个节点的入度
        int[] d = new int[numCourses];
        
        // 构建图并计算每个节点的入度
        for (int[] p : prerequisites) {
            g.get(p[1]).add(p[0]); // 添加从节点p[1]到节点p[0]的边
            d[p[0]]++; // 增加节点p[0]的入度
        }
        
        // 初始化队列用于BFS
        Queue<Integer> q = new LinkedList<>();
        int cnt = 0; // 计数器,记录可以遍历的节点数量
        
        // 将所有入度为0的节点添加到队列中
        for (int i = 0; i < numCourses; i++) {
            if (d[i] == 0) {
                q.offer(i);
                cnt++;
            }
        }
        
        // 执行BFS处理节点
        while (!q.isEmpty()) {
            int t = q.poll(); // 从队列中取出一个节点
            for (int v : g.get(t)) { // 遍历所有依赖于t的节点
                d[v]--; // 减少依赖节点的入度
                if (d[v] == 0) { // 如果依赖节点的入度变为0,将其加入队列
                    q.offer(v);
                    cnt++;
                }
            }
        }
        
        // 如果处理的节点数量等于总节点数,则表示可以遍历所有节点,否则不能
        return cnt == numCourses;
    }
}

 

华为实习2023042601

开发一个新的3D引擎,包含多个模块,每个模块负责不同的功能,比如渲染、物理、音效、网络等。 为了提高性能和稳定性,需要在游戏启动时初始化这些模块。 但是不同的模块之间存在依赖关系,比如渲染模块依赖于物理模块,音效模块依赖于网络模块等。如果不按照正确的顺序初始化这些模块,就会导致错误或崩溃。 需要开发一个代码分析工具,分析代码模块之间的依赖关系,确定模块的初始化顺序,判断是否有循环依赖等问题。 工具可以一次初始化一个或多个模块,只要它们之间没有依赖关系。 这个过程称为引擎模块初始化。 已经得到了一组模块间的依赖关系,需要计算引擎模块初始化的次数。

 

输入描述

  • 第一行是一个整数 `n`,表示模块总数。
  • 接下来的 `n` 行表示模块 `1` 到 `n` 的依赖关系。
  • 每行的第一个数是一个整数 `m`,表示依赖的模块数量,之后的数字表示当前模块依赖的模块ID。
  • 每一行的数字按空格分隔。

约束条件:

  • 1 ≤ m ≤ n ≤ 1000

输出描述

  • 输出批量初始化次数。
  • 若有循环依赖无法完成初始化,则输出 `-1`

样例

样例一

输入

5
3 2 3 4
1 5
1 5
1 5
0

  输出

3

解释

共 5 个模块。

模块 `1` 依赖模块 `2`、`3`、`4`;

模块 `2` 依赖模块 `5`;

模块 `3` 依赖模块 `5`;

模块 `4` 依赖模块 `5`;

模块 `5` 没有依赖。

批量初始化顺序为 `{5} -> {2, 3, 4} -> {1}`,共需 3 次批量初始化。

 

代码 

import java.util.*;

public class Main {
    public void solution(){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); // 读取模块总数
        int[] inDegree = new int[n + 1]; // 入度数组,用于记录每个模块的依赖计数
        inDegree[0] = -1; // 虚拟的0号节点,设置为-1
        ArrayList<Integer>[] outDegree = new ArrayList[n + 1]; // 出度表,用于记录每个模块的被依赖关系

        // 读取每个模块的依赖信息
        for(int i = 1; i <= n; ++i){
            int m = scanner.nextInt(); // 读取当前模块的依赖数量
            inDegree[i] += m; // 更新入度
            for(int j = 0; j < m; ++j){
                int from = scanner.nextInt(); // 读取依赖的模块ID
                if(outDegree[from] == null)
                    outDegree[from] = new ArrayList<>();
                outDegree[from].add(i); // 将当前模块添加到依赖列表中
            }
        }
        scanner.close(); 

        int res = 0; // 记录批量初始化次数
        boolean check; // 用于判断当前批次是否有可初始化的模块
        Queue<Integer> queue = new LinkedList<>(); // 队列用于BFS

        // 执行拓扑排序
        do {
            check = false;
            // 找出所有入度为0的模块
            for(int i = 1; i < inDegree.length; ++i){
                if(inDegree[i] == 0){
                    inDegree[i] = -1; // 标记为已处理
                    check = true;
                    queue.offer(i); // 将模块加入队列
                }
            }
            if(check)
                res += 1; // 增加批次计数
            // 处理队列中的模块
            while(!queue.isEmpty()){
                int from = queue.poll(); // 取出队首模块
                if(outDegree[from] == null)
                    continue;
                // 减少依赖模块的入度
                for(int i = 0; i < outDegree[from].size(); ++i)
                    --inDegree[outDegree[from].get(i)];
            }
        }while(check);

        // 检查是否存在未处理的模块
        for(int i = 1; i < inDegree.length; ++i){
            if(inDegree[i] != -1){
                System.out.println(-1); // 存在循环依赖,输出-1
                return;
            }
        }
        System.out.println(res); // 输出批量初始化次数
    }
    public static void main(String[] args) {
        Main m = new Main();
        m.solution(); // 调用解决方案
    }
}

 

华为秋招20230830

系统由n个任务组成,任务运行有依赖关系,前序任务执行完毕才可以启动后续任务。任务在启动前申请内存,执行完毕后释放,内存释放后可用于其他任务使用。

解除依赖后的任务会直接由操作系统调度,分配内存,进入运行状态。每个任务的运行时间相等。请计算系统所有任务执行所需要的最小内存。  

输入

第1行为1个正整数n,表示任务个数,n<20

第2行为n个正整数,表示每个任务所需要的内存大小,0<内存<10000

第3行为n个取值为0或1的数,表示任务0对其他任务的依赖关系,0表示不依赖,1表示依赖

....

第3+n行为n个取值为0或1的数,表示任务n−1对其他任务的依赖关系,0表示不依赖,1表示依赖

输出

输出系统任务执行所需要的最小内存

样例

输入

9
50 50 80 40 40 40 60 60 60
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 0 1 0 0 1 0 0 0
0 0 0 1 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 
0 0 0 1 0 0 0 0 0
0 0 0 0 0 0 1 1 0

输出

120

解释

样例解释

第一行:9,表示有 9 个任务

第二行:50 50 80 40 40 40 60 60 60,表示每个任务所需要的内存大小

  • t0 需要 50
  • t1 需要 50
  • t2 需要 80
  • t3 需要 40
  • t4 需要 40
  • t5 需要 40
  • t6 需要 60
  • t7 需要 60
  • t8 需要 60

第三行:0 0 0 0 0 0 0 0 0,表示 t0 不依赖任何任务

第四行:1 0 0 0 0 0 0 0 0,表示 t1 依赖 t0

第五行:0 1 0 0 0 0 0 0 0,表示 t2 依赖 t1

...依此类推

任务的关系用图表示:

 

  • 执行 t0,分配 m0 = 50,占用空间 [0, 50),最大访问地址为 50
  • 执行 t1,分配 m1 = 50,占用空间 [0, 50),最大访问地址为 50
  • 并发执行 t2t5,分配 m2 = 80m5 = 40,占用空间 [0, 120),最大访问地址为 120
  • 执行 t3,分配 m3 = 40,占用空间 [0, 40),最大访问地址为 40
  • 并发执行 t4t7,分配 m4 = 40m7 = 60,占用空间 [0, 100),最大访问地址为 100
  • 执行 t6,分配 m6 = 60,占用空间 [0, 60),最大访问地址为 60
  • 执行 t8,分配 m8 = 60,占用空间 [0, 60),最大访问地址为 60

输出系统需要的最小内存为 120

 

思路:拓扑排序+贪心

通过拓扑排序,我们可以确保每个任务按其依赖关系的顺序执行。每一轮计算并行执行的任务所需的总内存,并取这些总内存的最大值作为最终答案。这个方法能有效地解决问题并保证计算出的内存需求最小化。

 

import java.util.*;

public class Main {
    // 定义常量和变量
    static final int M = 20;  // 最大任务数量
    static int[] in = new int[M];  // 记录每个任务的入度
    static int[] a = new int[M];   // 记录每个任务所需的内存

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();  // 读取任务数
        ArrayList<Integer>[] e = new ArrayList[M];  // 邻接表,存储依赖关系

        // 读取每个任务的内存需求并初始化邻接表
        for (int i = 1; i <= n; i++) {
            a[i] = scanner.nextInt();
            e[i] = new ArrayList<>();
        }

        // 读取依赖关系矩阵并构建图
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                int t = scanner.nextInt();
                if (t != 0) {
                    in[i]++;  // 增加任务i的入度
                    e[j].add(i);  // 添加一条从j到i的边
                }
            }
        }

        // 初始化队列,用于拓扑排序
        Queue<Integer> q = new LinkedList<>();
        Queue<Integer> tmp = new LinkedList<>();  // 临时队列,用于保存本轮入度为0的任务
        for (int i = 1; i <= n; i++) {
            if (in[i] == 0) {
                q.add(i);  // 将初始入度为0的任务加入队列
            }
        }

        int ans = 0;  // 保存最终的最小内存需求
        // 开始拓扑排序
        while (!q.isEmpty()) {
            int sum = 0;  // 记录当前轮次所有任务的内存总和
            while (!q.isEmpty()) {
                int u = q.poll();
                sum += a[u];  // 累加当前任务的内存需求
                // 遍历当前任务的所有后继任务
                for (int i = 0; i < e[u].size(); i++) {
                    int v = e[u].get(i);
                    in[v]--;  // 将后继任务的入度减1
                    if (in[v] == 0) {
                        tmp.add(v);  // 如果后继任务的入度为0,加入临时队列
                    }
                }
            }
            ans = Math.max(ans, sum);  // 更新最大内存需求
            q.addAll(tmp);  // 将临时队列中的任务加入主队列,进行下一轮处理
            tmp.clear();  // 清空临时队列
        }

        // 输出最终计算出的最小内存需求
        System.out.println(ans);
    }
}

 

1.3 Dijkstra

求解从某个起点到达图上任意一点的最短路径(BFS 只能求解边权值为1)
import java.util.*;
import java.io.*;

class Main {
    static int N = 100010;  // 定义常量,最大点数

    static int n, m, idx;  // n为点数,m为边数,idx为边的编号
    static int[] h = new int[N];  // h数组,邻接表头结点
    static int[] w = new int[N];  // w数组,边权重
    static int[] e = new int[N];  // e数组,边指向的节点
    static int[] ne = new int[N];  // ne数组,邻接表下一个节点
    static int[] dist = new int[N];  // dist数组,记录1号点到各点的最短距离
    static boolean[] st = new boolean[N];  // st数组,记录每个点是否已确定最短路

    // 添加边的函数
    private static void add(int a, int b, int c) {
        e[idx] = b;  // 边的终点
        w[idx] = c;  // 边的权重
        ne[idx] = h[a];  // 当前边的下一个边
        h[a] = idx++;  // 更新头结点
    }

    public static void main(String[] args) throws Exception {
        // 使用BufferedReader和BufferedWriter进行输入输出
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
        
        // 读取第一个输入行
        String[] values = br.readLine().split("\\s+");
        n = Integer.parseInt(values[0]);
        m = Integer.parseInt(values[1]);
        
        // 初始化邻接表头结点
        Arrays.fill(h, -1);

        // 读取所有边的信息
        while (m-- > 0) {
            values = br.readLine().split("\\s+");
            int a = Integer.parseInt(values[0]);
            int b = Integer.parseInt(values[1]);
            int c = Integer.parseInt(values[2]);
            add(a, b, c);
        }

        // 计算1号点到n号点的最短路径
        int ret = dijkstra(n);

        // 输出结果
        log.write(ret + "\n");
        log.flush();
        log.close();
        br.close();
    }

    // 堆优化的Dijkstra算法
    private static int dijkstra(int n) {
        // 优先队列,按照到达节点的最短距离排序
        PriorityQueue<int[]> pq = new PriorityQueue<>(n, (a, b) -> a[1] - b[1]);
        int INF = 1 << 30;  // 定义一个无限大值
        Arrays.fill(dist, INF);  // 初始化距离数组,全部设为无穷大
        pq.offer(new int[]{1, 0});  // 将起点1加入优先队列
        dist[1] = 0;  // 起点到自己的距离为0

        // 主循环
        while (!pq.isEmpty()) {
            int[] cur = pq.poll();
            int ver = cur[0];  // 当前处理的节点
            int distance = cur[1];  // 当前节点的最短距离

            // 如果该节点已被处理,则跳过
            if (st[ver]) continue;

            st[ver] = true;  // 标记该节点为已处理

            // 遍历当前节点的所有邻边
            for (int i = h[ver]; i != -1; i = ne[i]) {
                int j = e[i];  // 目标节点
                // 如果找到更短的路径,则更新
                if (dist[j] > distance + w[i]) {
                    dist[j] = distance + w[i];
                    pq.offer(new int[]{j, dist[j]});  // 将更新后的节点加入优先队列
                }
            }
        }

        // 如果n号点的最短路径仍为无穷大,说明不可达
        return dist[n] == INF ? -1 : dist[n];
    }
}

  

 

华为2023082303

有一些镜子,能够吸收光芒并在一定时间后散射。

镜子分为一级镜和二级镜,一级镜散射速度为1ms,二级镜为2ms。

将镜子放在一个二维矩阵中,每个镜子的坐标为整数。

现在给某个镜子一道光芒,最早什么时候所有镜子都能吸收到光芒?

注:矩阵的下标从0开始。

 

输入描述:

 

矩阵的列数  n ( n≤500 )

矩阵的行数  m  ( m ≤ 500 )

最初获得光芒的镜子的坐标  (i, j) 

接下来  m  行,每行  n  个数字,代表该位置镜子的等级:

  • 0 表示该位置是一堵密不透光的墙
  • 1 表示该位置的镜子散射耗时1ms
  • 2 表示该位置的镜子散射耗时2ms

 

输出描述:

一个数字代表最小时间。如果有镜子不能够吸收到光芒,输出-1。

 

样例:

输入:

5

5

2 2

1 0 2 1 0

2 2 1 2 0

0 0 1 0 0

2 1 1 0 0

1 1 1 1 1

  

输出:

6

 

思路:

找到所有镜子接收到光芒的最早时间=>单源最短路算法:Dijkstra:

  1. 每一步,从队列中取出时间最早的镜子。
  2. 更新其四个方向镜子的接收时间。
  3. 如果新的接收时间比当前的接收时间早,则更新接收时间,并将新的镜子加入队列。
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        // 读取输入,直到没有更多的整数输入
        while (in.hasNextInt()) {
            int n = in.nextInt(); // 矩阵的行数
            int m = in.nextInt(); // 矩阵的列数

            int[][] mirrors = new int[n][m]; // 存储镜子等级的矩阵
            int initI = in.nextInt(); // 初始光芒镜子的行坐标
            int initJ = in.nextInt(); // 初始光芒镜子的列坐标

            // 读取矩阵中的镜子等级
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    mirrors[i][j] = in.nextInt();
                }
            }

            int[][] times = new int[n][m]; // 存储每个镜子接收到光芒的时间
            // 初始化时间矩阵,初始值设为一个较大的数
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    times[i][j] = Integer.MAX_VALUE;
                }
            }

            // 优先队列,用于进行Dijkstra算法
            Queue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
                @Override
                public int compare(int[] o1, int[] o2) {
                    return o1[2] - o2[2]; // 按时间排序
                }
            });

            int[][] visited = new int[n][m]; // 标记每个镜子是否已经接收到光芒
            queue.offer(new int[]{initI, initJ, 0}); // 将初始镜子加入队列,时间设为0

            // Dijkstra算法
            while (!queue.isEmpty()) {
                int[] poll = queue.poll(); // 取出时间最早的镜子
                int x = poll[0];
                int y = poll[1];
                int time = poll[2];

                // 边界条件检查
                if (x < 0 || x >= mirrors.length || y < 0 || y >= mirrors[0].length) {
                    continue;
                }
                // 如果是墙则跳过
                if (mirrors[x][y] == 0) {
                    continue;
                }
                // 如果已经访问过则跳过
                if (visited[x][y] == 1) {
                    continue;
                }

                visited[x][y] = 1; // 标记为已访问
                times[x][y] = time; // 更新当前镜子的时间

                // 将四个方向的镜子加入队列,并更新时间
                queue.offer(new int[]{x - 1, y, time + mirrors[x][y]});
                queue.offer(new int[]{x + 1, y, time + mirrors[x][y]});
                queue.offer(new int[]{x, y - 1, time + mirrors[x][y]});
                queue.offer(new int[]{x, y + 1, time + mirrors[x][y]});
            }

            int max = 0; // 用于找出接收到光芒的最晚时间
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    // 只考虑不是墙的镜子
                    if (mirrors[i][j] != 0) {
                        max = Math.max(max, times[i][j]);
                    }
                }
            }

            // 如果有镜子不能接收到光芒,则返回-1
            if (max == Integer.MAX_VALUE) {
                System.out.println(-1);
            } else {
                System.out.println(max); // 输出最早时间
            }
        }
    }
}

 

饿了么2023081703

有一个迷宫,有 `n` 个地点,通过 `m` 条道路联通。每次需要从起点(1号节点)取一面旗帜送到指定地点,然后返回起点,直到将所有旗帜送完。

你需要知道送完所有旗帜的最短路程是多少。

输入描述

  1. 第一行输入三个整数 `n`, `m`, `q`,分别表示地点数、路径数和需要送达旗帜的地点数。
  2. 接下来 `m` 行,每行输入三个整数 `u`, `v`, `w`,表示地点 `u` 和地点 `v` 之间有一条长度为 `w` 的道路。
  3. 最后一行输入 `q` 个整数,表示需要送达旗帜的 `q` 个地点。

输出描述
一个整数表示最短总路程。

示例
输入

4 3 3
1 2 1
2 3 2
3 4 3
2 3 4

  

 输出

20

  

说明
从 1 号点到 2 号点再回来,路程距离为 2。
再从 1 号点到 3 号点再回来,路程距离为 6。
最后从 1 号点到 4 号点再回来,路程距离为 12。

 思路:

利用Dijkstra,初始化从起点到其余n-1个节点最短路径dist,最后遍历并累加 dist[qi]*2即可。

  

import java.util.*;

public class Main {
    public static void main(String[] args) {
        final int INF = 0x3f3f3f3f; // 定义无穷大
        Scanner scanner = new Scanner(System.in);
        
        int n = scanner.nextInt(); // 地点数
        int m = scanner.nextInt(); // 路径数
        int T = scanner.nextInt(); // 需要送达旗帜的地点数

        List<List<PII>> g = new ArrayList<>(); // 图的邻接表表示
        for (int i = 0; i < n; i++) {
            g.add(new ArrayList<>());
        }

        // 读取图的边
        for (int i = 0; i < m; i++) {
            int u = scanner.nextInt() - 1; // 起点
            int v = scanner.nextInt() - 1; // 终点
            int w = scanner.nextInt();     // 边的权重
            g.get(u).add(new PII(v, w));
            g.get(v).add(new PII(u, w));
        }

        // 优先队列,用于Dijkstra算法
        PriorityQueue<PII> q = new PriorityQueue<>(Comparator.comparingInt(PII::getFirst));

        int[] st = new int[n]; // 标记数组,记录每个节点是否已确定最短路径
        int[] dist = new int[n]; // 最短路径数组
        Arrays.fill(dist, INF); // 初始化最短路径为无穷大
        dist[0] = 0; // 起点的最短路径为0

        q.offer(new PII(0, 0)); // 将起点加入队列
        while (!q.isEmpty()) {
            PII top = q.poll(); // 取出优先队列中的最小元素
            int u = top.getSecond(); // 当前节点
            if (st[u] == 1) continue; // 如果该节点已经确定最短路径,则跳过
            st[u] = 1; // 标记该节点

            for (PII p : g.get(u)) { // 遍历邻接节点
                int v = p.getFirst(); // 邻接节点
                int w = p.getSecond(); // 边的权重
                if (dist[v] > dist[u] + w) { // 更新最短路径
                    dist[v] = dist[u] + w;
                    q.offer(new PII(dist[v], v)); // 将更新后的节点加入队列
                }
            }
        }

        long ans = 0; // 记录总的最短路程
        while (T-- > 0) { // 读取每个需要送达旗帜的地点
            int x = scanner.nextInt() - 1;
            ans += dist[x]; // 累加从起点到该地点的最短路径
        }
        System.out.println(ans * 2); // 输出总的往返路程
    }

    // 辅助类,存储节点及其对应的权重
    static class PII {
        private final int first;
        private final int second;

        public PII(int first, int second) {
            this.first = first;
            this.second = second;
        }

        public int getFirst() {
            return first;
        }

        public int getSecond() {
            return second;
        }
    }
}

  

  

  

 

标签:图论,scanner,int,static,模块,new,节点
From: https://www.cnblogs.com/alohablogs/p/18155472

相关文章

  • 最短路图论
    dijkstraCode:#include<bits/stdc++.h>usingnamespacestd;typedefpair<int,int>pii;constintN=1e5+5,inf=INT_MAX;intn,m,dis[N],s;//structnode{//intfrom,to,w,val;//};boolvis[N];vector<pii>edge......
  • 知识点整理 - 连通性相关 | 《综述图论中连通性及相关问题的一些处理方法》学习笔记
    是ix35老师论文的学习笔记,同时也用作连通性相关知识梳理。可能不会包含很多定义,只会挑重要的写。会包含一些例题。定义与记号\(u\rightsquigarrowv\)代表\(u\)到\(v\)的一条路径。有时也会用这个记号表示连通性。无向图点/边连通度:若\(u,v\)任意点割集大小不小......
  • 图论-最近公共祖先
    例题:祖孙询问 给定一棵包含 n......
  • 动态规划--图论中使实用场景概述
    目录一 动态规划概述二 动态规划在图论中应用场景三c实例1.**最短路径问题(Dijkstra算法)**:2.**最小生成树问题(Kruskal算法)**:一 动态规划概述动态规划(DynamicProgramming,简称DP)是一种用于解决具有重叠子问题和最优子结构特性的问题的优化方法。动态规划通过将原......
  • 【图论】割点(割顶)
    前置定义有无向图\(G=(V,E).\)无向图的DFS树:从某一点\(root\)开始DFS,访问邻点\(.\)当搜索到点\(u\)时,我们遍历每一条以\(u\)为起点的边\((u,v_i)\),且定义有向边\(u\longrightarrowv_i.\)于是DFS的过程全部完成之后,所有被定义的有向边就会组成一颗以\(r......
  • 图论定理汇总(二)
    第六章平面图(一)、平面图的概念定义1如果能把图GGG画在平面上,使得除顶点外,边与边之间没有交叉,称G......
  • 图论-二分图匹配匈牙利算法
    不得不说,如果以现实角度代入此算法的理解,就合理了很多,虽然有悖道德准则重点在于以下几点每次给女生分配男生前,都把男生全部初始化为可预定状态(即使他已经被别人成功匹配了)在所有女生中意的男生中遍历,如果发现该男生可预订就先预定,然后看他是否已经有主了,如果有主了,就dfs(matc......
  • 图论-割边与边双连通分量
    首先是两篇模板割边点击查看代码inthead[N],cnt=1;structEdge{intfrom,to,nxt;}e[N<<1];voidadd(intu,intv){e[++cnt].from=u;e[cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;}intdfn[N],back[N],tim,bri_cnt;//dfn......
  • 图论-割点与点双连通分量
    首先是两篇的代码割点点击查看代码inthead[N],cnt=0;structEdge{intfrom,to,nxt;}e[N<<1];voidadd(intu,intv){e[++cnt].from=u;e[cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;}intdfn[N],back[N],tim;//dfn[i]时......
  • 关于 图论建模 的一些技巧
    分层图思想分层图在最短路中经常用到。直观上讲,就是将一个图复制k倍,互相是平行的,即互不影响,分层图两两之间会有决策边相连。这就等价于要在一个图上进行k次决策,对于每次决策,不影响图的结构,只影响目前的状态或代价。一般将决策前的状态和决策后的状态之间连接一条权值......