1.图论基础

  1. 图论在实际笔试中考的不多,但它的经典算法⽐较多,⽐如什么最⼩⽣成树,最短路径,拓扑排序,⼆分图 判定之类的。 <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22411805/1644747141870-b044d88f-2932-40c6-9933-480e0b17a93f.png#clientId=u3b266a2d-1afa-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=398&id=u7e669f27&margin=%5Bobject%20Object%5D&name=image.png&originHeight=398&originWidth=486&originalType=binary&ratio=1&rotation=0&showTitle=false&size=74072&status=done&style=none&taskId=ud7393e97-ed4f-44be-88d7-49f9650bf80&title=&width=486)<br />在这幅图中,每一个节点都指向了其他的节点,我们称之为**有向图**,边的指向是单向的。

我们通常用邻接表和邻接矩阵来存储图这种结构。
image.png
一般做题来说,我们倾向于使用邻接表这种存储方式,可以用map实现,也可以用List实现,只要能够实现如上图中邻接表的对应关系就行。如map就是键值对,List就是list数组中的每个list[i]=new LinkedList();

// 邻接矩阵
// graph[x] 存储 x 的所有邻居节点
List<Integer>[] graph;

// 邻接矩阵
// matrix[x][y] 记录 x 是否有⼀条指向 y 的边
boolean[][] matrix;

2.Leetcode797.所有可能的路径

image.png
题目给了graph数组的定义,graph[1]表示和1相邻的节点的列表,那么我们用dfs深搜,每次搜到一个不重复的就添加进路径列表,直到i==n-1(n为graph的长度),表示已经到达最后一个位置。将路径列表添加进结果列表。
注意,我们的图都是从0开始出发的,所以我们的路径列表先添加1个0

class Solution {

    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        List<Integer> path=new ArrayList<>();
        List<List<Integer>> res=new ArrayList<>();
        path.add(0);
        dfs(graph,res,path,0);
        return res;
    }

    public void dfs(int[][] graph,List<List<Integer>> res,List<Integer> path,int start){
        int n=graph.length;
        if(start==n-1){
            //path.add(start);
            res.add(new ArrayList(path));
            return;
        }
        for(int i:graph[start]){
            path.add(i);
            dfs(graph,res,path,i);
            path.remove(path.size()-1);
        }
    }
}

3.Leetcode207.课程表

image.png
根据题目输入的数组,我们可以画出如下的图:
image.png
知道了numCourses,相当于我们知道了有多少个节点,那么我们就创建
List[ ] subject=new LinkedList[ numCourses ];每个索引对应的位置再new一个LinkedList,用来储存与该索引节点相邻的节点。
判断我们的这个函数是否能够学完所有的课程,类同于判断一个有向图是否包含环,因为一旦图中有了环,我们dfs的时候如果走到这个节点,就会无休止的在这个环里面走下去,那么我们如何避免dfs走之前再走过的路呢?
很简单,定义一个boolean类型的path数组,遍历过程中,走过了一个节点就把visited设置为true,在dfs函数中开头添加一个判断条件,如果这次深搜的节点已经出现过了,那就不用继续搜索了,说明图中包含环,直接return就完事了。我们还需要定义一个visited数组表示遍历的起点,因为我们不确定从哪个起点开始可以遍历完整张图,所以要全部尝试,同理,我们再dfs之前再次判断visited是否为true,如果是true的话,说明这个起点已经遍历过了,我们直接return 结束遍历就好了。
读者可能会有疑惑:如果出现图中的节点是分离的情况呢?那么无论从哪个节点开始,不是都不能遍历完整个图吗?
我们再仔细的看看题意:image.png,“可能”,那么结果就一目了然了,就是这么做的。
image.png image.png

class Solution {
    boolean hascycle=false;
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<Integer>[] subject=new LinkedList[numCourses];
        boolean[] path=new boolean[numCourses];
        boolean[] visited=new boolean[numCourses];
        build(subject,prerequisites,numCourses);
        for(int i=0;i<numCourses;i++){
            dfs(subject,visited,i,path);
        }

        return !hascycle;
    }
    public void build(List<Integer>[] subject,int[][] prerequisites,int numCourses){ //构建邻接表
        for(int i=0;i<numCourses;i++){
            subject[i]=new LinkedList<Integer>();
        }
        for(int[] i:prerequisites){
            int from=i[1];
            int to=i[0];
            subject[from].add(to);
        }
    }

    public void dfs(List<Integer>[] subject,boolean[] visited,int start,boolean[] path){
        if(path[start]){
            hascycle=true;
        }
        if(hascycle || visited[start]){
            return;
        }
        visited[start]=true;
        path[start]=true;
        for(int i:subject[start]){
            dfs(subject,visited,i,path);
        }
        path[start]=false;
    }
}

4.Leetcode210.课程表Ⅱ

image.png
拓扑排序:后续遍历反转之后的结果!!!!!只针对无环有向图,需要进行环检测。

class Solution {
    boolean hascycle=false;
    List<Integer> res=new LinkedList<>();
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        List<Integer>[] subject=new LinkedList[numCourses];
        boolean[] visited=new boolean[numCourses];
        boolean[] path=new boolean[numCourses];
        build(subject,prerequisites,numCourses);
        for(int i=0;i<numCourses;i++){
            dfs(subject,visited,i,path);
        }
        if(hascycle)return new int[]{};
        Collections.reverse(res);
        int[] tmp=new int[numCourses];
        for(int i=0;i<numCourses;i++){
            tmp[i]=res.get(i);
        }
        return tmp;

    }

        public void build(List<Integer>[] subject,int[][] prerequisites,int numCourses){ //构建邻接表
        for(int i=0;i<numCourses;i++){
            subject[i]=new LinkedList<Integer>();
        }
        for(int[] i:prerequisites){
            int from=i[1];
            int to=i[0];
            subject[from].add(to);
        }
    }

        public void dfs(List<Integer>[] subject,boolean[] visited,int start,boolean[] path){
        int n=subject.length;
        if(path[start]){
            hascycle=true;
        }
        if(hascycle || visited[start]){
            return;
        }
        visited[start]=true;
        path[start]=true;
        for(int i:subject[start]){
            dfs(subject,visited,i,path);
        }
        res.add(start);
        path[start]=false;
    }
}

5.Kruskal算法(克鲁斯卡尔)

Kruskal算法要与并查集知识连接起来

1.并查集(Union-find)模板

class UF{
    /**
    1.构造函数,用于创建联通分量count,parent数组和size数组
    2.创建一个方法union用于连接
    3.创建一个find方法用于寻找联通分量的根节点
    4.创建一个isconnected方法检测两个节点是否连接
    5.创建一个count方法,用于返回联通分量的数值
     */
    private int count;  //联通分量
    private int[] parent;       //存储一棵树
    private int[] size;     //记录以下标为根节点的树的重量(节点数)

    public UF(int n){
        this.count=n;
        parent=new int[n];
        size=new int[n];
        for(int i=0;i<n;i++){
            parent[i]=i;
            size[i]=1;
        }
    }

    public boolean isconnected(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        return rootp==rootq;
    }

    public void union(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        if(rootp==rootq){
            return;
        }
        if(size[rootp]>size[rootq]){
            parent[rootq]=rootp;
            size[rootp]+=size[rootq];
        }else{
            parent[rootp]=rootq;
            size[rootq]+=size[rootp];
        }
        count--;
    }

    public int find(int x){
        while(parent[x]!=x){
            parent[x]=parent[parent[x]];    //可加可不加,用于进行路径压缩,减少时间复杂度
            x=parent[x];
        }
        return x;
    }

    public int count(){
        return count;
    }
}

2.1Leetcode.261以图判树

image.png

class Solution {
    public boolean validTree(int n, int[][] edges) {
        UF uf=new UF(n);
        for(int[] edge:edges){
            int p=edge[0];
            int q=edge[1];
            if(uf.isconnected(p,q)){
                return false;
            }
            uf.union(p,q);
        }
        return uf.count()==1;
    }
}

class UF{
        /**
    1.构造函数,用于创建联通分量count,parent数组和size数组
    2.创建一个方法union用于连接
    3.创建一个find方法用于寻找联通分量的根节点
    4.创建一个isconnected方法检测两个节点是否连接
    5.创建一个count方法,用于返回联通分量的数值
     */
    private int count;
    private int[] size;
    private int[] parent;
    public UF(int n){
        this.count=n;
        parent=new int[n];
        size=new int[n];
        for(int i=0;i<n;i++){
            parent[i]=i;
            size[i]=1;
        }
    }

    public void union(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        if(rootp==rootq){
            return;
        }else{
            if(size[rootp]>size[rootq]){
                parent[rootq]=rootp;
                size[rootp]+=size[rootq];
            }else{
                parent[rootp]=rootq;
                size[rootq]+=size[rootp];
            }
        }
        count--;
    }

    public boolean isconnected(int p ,int q){
        int rootp=find(p);
        int rootq=find(q);
        return rootp==rootq;
    }

    public int find(int x){
        while(parent[x]!=x){
            parent[x]=parent[parent[x]];
            x=parent[x];
        }
        return x;
    }

    public int count(){
        return count;
    }

}

image.png

2.最小生成树问题

Leetcode1135.最低成本联通所有城市

image.png

class Solution {
    public int minimumCost(int n, int[][] connections) {
        int min=0;
        UF uf=new UF(n+1);
        Arrays.sort(connections,(a,b)->(a[2]-b[2]));
        for(int[] edge:connections){
            int p=edge[0];
            int q=edge[1];
            int weight=edge[2];
            if(uf.isconnected(p,q)){
                continue;
            }
            min+=weight;
            uf.union(p,q);
        }
        return uf.count()==2?min:-1;
    }
}

class UF{
    /**
    1.构造函数,用于创建联通分量count,parent数组和size数组
    2.创建一个方法union用于连接
    3.创建一个find方法用于寻找联通分量的根节点
     */
    private int count;  //联通分量
    private int[] parent;       //存储一棵树
    private int[] size;     //记录以下标为根节点的树的重量(节点数)

    public UF(int n){
        this.count=n;
        parent=new int[n];
        size=new int[n];
        for(int i=0;i<n;i++){
            parent[i]=i;
            size[i]=1;
        }
    }

    public boolean isconnected(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        return rootp==rootq;
    }

    public void union(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        if(rootp==rootq){
            return;
        }
        if(size[rootp]>size[rootq]){
            parent[rootq]=rootp;
            size[rootp]+=size[rootq];
        }else{
            parent[rootp]=rootq;
            size[rootq]+=size[rootp];
        }
        count--;
    }

    public int find(int x){
        while(parent[x]!=x){
            parent[x]=parent[parent[x]];    //可加可不加,用于进行路径压缩,减少时间复杂度
            x=parent[x];
        }
        return x;
    }

    public int count(){
        return count;
    }
}

image.png

Leetcode.1584连接所有点的最小费用

image.png

class Solution {
    public int minCostConnectPoints(int[][] points) {
        int n=points.length;
        int length=0;
        UF uf=new UF(n);
        List<int[]> edges=new ArrayList<>();
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                int xi=points[i][0];
                int yi=points[i][1];
                int xj=points[j][0];
                int yj=points[j][1];
                edges.add(new int[]{i,j,Math.abs(xi-xj)+Math.abs(yi-yj)});
            }
        }
        Collections.sort(edges,(a,b)->(a[2]-b[2]));
        for(int[] edge:edges){
            int p=edge[0];
            int q=edge[1];
            int weight=edge[2];
            if(uf.isconnected(p,q)){
                continue;
            }else{
                length+=weight;
                uf.union(p,q);
            }
        } 
        return length;   
    }
}

class UF{
    private int count;
    private int[] size;
    private int[] parent;
    public UF(int n){
        this.count=n;
        size=new int[n];
        parent=new int[n];
        for(int i=0;i<n;i++){
            parent[i]=i;
            size[i]=1;
        }
    }

    public boolean isconnected(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        return rootp==rootq;
    }

    public int find(int x){
        while(parent[x]!=x){
            parent[x]=parent[parent[x]];
            x=parent[x];
        }
        return x;
    }

    public void union(int p,int q){
        int rootp=find(p);
        int rootq=find(q);
        if(isconnected(p,q)){
            return;
        }else{
            if(size[rootp]>size[rootq]){
                parent[rootq]=rootp;
                size[rootp]+=size[rootq];
            }else{
                parent[rootp]=rootq;
                size[rootq]+=size[rootp];
            }
            count--;
        }
    }
}

image.png

6.Dijkstra算法(迪杰斯特拉算法)

1.算法模板

// 返回节点 from 到节点 to 之间的边的权重
int weight(int from, int to);

// 输入节点 s 返回 s 的相邻节点
List<Integer> adj(int s);

// 输入一幅图和一个起点 start,计算 start 到其他节点的最短距离
int[] dijkstra(int start, List<Integer>[] graph) {
    // 图中节点的个数
    int V = graph.length;
    // 记录最短路径的权重,你可以理解为 dp table
    // 定义:distTo[i] 的值就是节点 start 到达节点 i 的最短路径权重
    int[] distTo = new int[V];
    // 求最小值,所以 dp table 初始化为正无穷
    Arrays.fill(distTo, Integer.MAX_VALUE);
    // base case,start 到 start 的最短距离就是 0
    distTo[start] = 0;

    // 优先级队列,distFromStart 较小的排在前面
    Queue<State> pq = new PriorityQueue<>((a, b) -> {
        return a.distFromStart - b.distFromStart;
    });

    // 从起点 start 开始进行 BFS
    pq.offer(new State(start, 0));

    while (!pq.isEmpty()) {
        State curState = pq.poll();
        int curNodeID = curState.id;
        int curDistFromStart = curState.distFromStart;

        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }
        // 将 curNode 的相邻节点装入队列
        for (int nextNodeID :   (curNodeID)) {
            // 看看从 curNode 达到 nextNode 的距离是否会更短
            int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);
            if (distTo[nextNodeID] > distToNextNode) {
                // 更新 dp table
                distTo[nextNodeID] = distToNextNode;
                // 将这个节点以及距离放入队列
                pq.offer(new State(nextNodeID, distToNextNode));
            }
        }
    }
    return distTo;
}



class State {
    // 图节点的 id
    int id;
    // 从 start 节点到当前节点的距离
    int distFromStart;

    State(int id, int distFromStart) {
        this.id = id;
        this.distFromStart = distFromStart;
    }
}

2.最短路径问题

Leetcode.743网络延迟时间

image.png
分析:我们通常用dijkstra算法来求最短路径的问题,前提是:抽象化的图不是负权图(该算法无法求负权图的最短路径;对于该题:我们根据题意构造一个有关图的邻接表,因为有n个网络节点,并且序号从1开始,所以我们构建的临界表的数组长度为n+1:
List<int[]>[] graph=new LinkedList[n+1];
我们邻接表中的数据首先必须要有当前节点的邻居节点,又因为求最短路径,所以邻接表中还应该存储当前节点到邻居节点的权值,因此我们的graph[]中存储的应该是以int【目标邻居节点,权值】为单位的元素,为了方便我们对数据进行存取,我们用LinkedList<>实现我们graph数组的每一行数据:

for(int i=1;i<graph.length;i++){
            graph[i]=new LinkedList<int[]>();
        }

遍历一遍初始times[ ][ ]数组,将对应的元素添加进graph[ ]数组中:

        for(int[] edge:times){
            int from=edge[0];
            int to=edge[1];
            int weight=edge[2];
            graph[from].add(new int[]{to,weight});
        }

根据题意,我们用优先队列实现dijkstra算法,首先要明确,这个算法需要什么参数:
1.起始点
2.一幅图(邻接表)
我们上面创建的邻接表就可以在这里发挥作用了
为什么要用优先队列,想象图为一个多叉树,我们把根节点的邻居节点加入到队列中,但是我们无法确定根节点到每个邻居节点的大小,所以我们利用优先队列按照升序排序,这样每次poll()的时候我们都能拿到理想的“潜力”最大的元素(更有可能接近最短路径的元素)
初始distTo【】数组(存储start到目标点的最短路径长度)每个元素设置为Integer.MAX_VALUE。
根据定义,start到start的权值为0(距离为0),所以distTo【start】=0;
每次poll()一个元素,我们记录他的id和起始点到该点的距离

int[] distTo=new int[graph.length];     //从start到当前节点的距离
Arrays.fill(distTo,Integer.MAX_VALUE);
distTo[start]=0;        //start到start的距离为0
pq.add(new State(start,0));
while(!pq.isEmpty()){
            State curState=pq.poll();
            int curNodeId=curState.id;
            int curdisFromStart=curState.disFromStart;
            if(curdisFromStart>distTo[curNodeId]){
                // 已经有⼀条更短的路径到达 curNodeId 节点了
                continue;
            }

如果检测到有更短的路径,那么提取graph[curNodeId]中的元素,找到当前最短路径+到下一个节点的权值=最小的数值,更新distTo【】数组

for(int[] neighbor:graph[curNodeId]){
                int nextNodeid=neighbor[0];
                int nextdistTo=neighbor[1]+distTo[curNodeId];
                if(distTo[nextNodeid]>nextdistTo){
                    distTo[nextNodeid]=nextdistTo;
                    pq.add(new State(nextNodeid,nextdistTo));
                }
            }

所有的元素遍历完后,生成distTo【】数组,返回。
最后只要遍历distTo【】数组,如果有一个数=Integer.MAX_VALUE则说明有一个节点没有达到,则return-1;

class Solution {
    public int networkDelayTime(int[][] times, int n, int k) {
        List<int[]>[] graph=new LinkedList[n+1];
        for(int i=1;i<graph.length;i++){
            graph[i]=new LinkedList<int[]>();
        }
        for(int[] edge:times){
            int from=edge[0];
            int to=edge[1];
            int weight=edge[2];
            graph[from].add(new int[]{to,weight});
        }
        int res=0;
        int distTo[]=dijkstra(k,graph);
        for(int i=1;i<graph.length;i++){
            if(distTo[i]==Integer.MAX_VALUE){
                return -1;
            }
            res=Math.max(res,distTo[i]);
        }
        return res;
    }

    public int[] dijkstra(int start,List<int[]>[] graph){
        int[] distTo=new int[graph.length];     //从start到当前节点的距离
        Arrays.fill(distTo,Integer.MAX_VALUE);
        Queue<State> pq=new PriorityQueue<>((a,b)->{
            return a.disFromStart-b.disFromStart;
            });
        distTo[start]=0;        //start到start的距离为0
        pq.add(new State(start,0));
        while(!pq.isEmpty()){
            State curState=pq.poll();
            int curNodeId=curState.id;
            int curdisFromStart=curState.disFromStart;
            if(curdisFromStart>distTo[curNodeId]){
                continue;
            }
            for(int[] neighbor:graph[curNodeId]){
                int nextNodeid=neighbor[0];
                int nextdistTo=neighbor[1]+distTo[curNodeId];
                if(distTo[nextNodeid]>nextdistTo){
                    distTo[nextNodeid]=nextdistTo;
                    pq.add(new State(nextNodeid,nextdistTo));
                }
            } 
        }
        return distTo;
    }
}

class State{
    int disFromStart;
    int id;

    public State(int id,int disFromStart){
        this.id=id;
        this.disFromStart=disFromStart;
    }
}

蓝桥杯2021省赛:路径

image.png
标准的最短路问题,主要细节就是:结合了数论最大公约数和最小公倍数的求法,其他的就套dijkstra模板就好了

  • 最大公约数与最小公倍数

      public static int gcd(int m, int n) { // 最大公约数
          if (m % n == 0)
              return n;
          return gcd(n, m % n);
      }
    
      public static int lcm(int m, int n) { // 最小公倍数
          return m * n / gcd(m, n);
      }
    

    ```java package lanqiao;

import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.PriorityQueue; import java.util.Queue;

public class Main {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    // 构建邻接表
    List<int[]>[] graph = new LinkedList[2022];
    for (int i = 0; i < graph.length; i++) {
        graph[i] = new LinkedList<int[]>();
    }
    for (int i = 1; i <= 2021; i++) {
        for (int j = 1; j <= 2021; j++) {
            if (i == j) {
                continue;
            } else if (Math.abs(i - j) > 21) {
                continue;
            } else if (Math.abs(i - j) <= 21) {
                graph[i].add(new int[] { j, lcm(i, j) });
            }
        }
    }

    int[] disTo = dijkstra(graph, 1);
    System.out.println(disTo[2021]);
}

public static int[] dijkstra(List<int[]>[] graph, int start) {
    int[] disTostart = new int[graph.length];
    Queue<State> q = new PriorityQueue<State>((a, b) -> {
        return a.disFromstart - b.disFromstart;
    });
    Arrays.fill(disTostart, Integer.MAX_VALUE);
    disTostart[start] = 0;
    q.add(new State(start, 0));
    while (!q.isEmpty()) {
        State curNode = q.poll();
        int curId = curNode.id;
        int curdisFromstart = curNode.disFromstart;
        if (curdisFromstart > disTostart[curId]) {
            continue;
        }
        for (int[] neighbor : graph[curId]) {
            int nextNodeid = neighbor[0];
            int nextNodedis = disTostart[curId] + neighbor[1];
            if (nextNodedis < disTostart[nextNodeid]) {
                disTostart[nextNodeid] = nextNodedis;
                q.add(new State(nextNodeid, nextNodedis));
            }
        }
    }
    return disTostart;
}

public static int gcd(int m, int n) { // 最大公约数
    if (m % n == 0)
        return n;
    return gcd(n, m % n);
}

public static int lcm(int m, int n) { // 最小公倍数
    return m * n / gcd(m, n);
}

}

class State { int id; int disFromstart;

public State(int id, int disFromstart) {
    this.id = id;
    this.disFromstart = disFromstart;
}

}

<a name="tX3J8"></a>
# 7.Floyd算法(动态规划思想)

<a name="VVew6"></a>
## 1.算法模板:
```java
public class FloydAlgorithm {
    public static int MaxValue = 100000;
    public static int[][] path;
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入顶点数和边数:");
        //顶点数
        int vertex = input.nextInt();
        //边数
        int edge = input.nextInt();

        int[][] matrix = new int[vertex][vertex];
        //初始化邻接矩阵
        for (int i = 0; i < vertex; i++) {
            for (int j = 0; j < vertex; j++) {
                matrix[i][j] = MaxValue;
            }
        }

        //初始化路径数组
        path = new int[matrix.length][matrix.length];

        //初始化边权值
        for (int i = 0; i < edge; i++) {
            System.out.println("请输入第" + (i + 1) + "条边与其权值:");
            int source = input.nextInt();
            int target = input.nextInt();
            int weight = input.nextInt();
            matrix[source][target] = weight;
        }

        //调用算法计算最短路径
        floyd(matrix);
    }

    //非递归实现
    public static void floyd(int[][] matrix) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                path[i][j] = -1;
             }
        }

        for (int m = 0; m < matrix.length; m++) {
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix.length; j++) {
                    if (matrix[i][m] + matrix[m][j] < matrix[i][j]) {
                        matrix[i][j] = matrix[i][m] + matrix[m][j];
                        //记录经由哪个点到达
                        path[i][j] = m;
                    }
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                if (i != j) {
                    if (matrix[i][j] == MaxValue) {
                        System.out.println(i + "到" + j + "不可达");
                    } else {
                        System.out.print(i + "到" + j + "的最短路径长度是:" + matrix[i][j]);
                        System.out.print("最短路径为:" + i + "->");
                        findPath(i, j);
                        System.out.println(j);
                    }
                }
            }
        }
    }

    //递归寻找路径
    public static void findPath(int i, int j) {
        int m = path[i][j];
        if (m == -1) {
            return;
        }

        findPath(i, m);
        System.out.print(m + "->");
        findPath(m, j);
    }
}

2.蓝桥杯2021省赛:路径

image.png

package lanqiao;

import java.util.Arrays;
import java.util.Scanner;

/**
 * 小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
 * 
 * 小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
 * 
 * 对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于
 * 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
 * 
 * 例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为
 * 75。
 * 
 * 请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
 * 
 * 提示:建议使用计算机编程解决问题。
 * 
 * @author luzhendong
 *
 */
public class Floyd {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();// 节点个数
        // int bian = sc.nextInt();// 边的个数
        int[][] matrix = new int[N + 1][N + 1];
        int[][] path = new int[N + 1][N + 1];
//        for (int i = 0; i < matrix.length; i++) {
//            Arrays.fill(matrix[i], Integer.MAX_VALUE);
//        }

//        for (int i = 0; i < bian; i++) {
//            System.out.println("请输入第" + (i + 1) + "条边与其权值:");
//            int source = sc.nextInt();
//            int target = sc.nextInt();
//            int weight = sc.nextInt();
//            matrix[source][target] = weight;
//        }
        for (int i = 0; i <= N; i++) {
            for (int j = i + 1; j <= N; j++) {
                if (Math.abs(i - j) <= 21) {
                    matrix[i][j] = lcm(i, j);
                    matrix[j][i] = lcm(i, j);
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][j] = 1000000000;
                }
            }
        }

//        for (int i = 0; i < matrix.length; i++) {
//            for (int j = 0; j < matrix[0].length; j++) {
//                System.out.print(matrix[i][j] + " ");
//            }
//            System.out.println();
//        }

        for (int j = 0; j < path.length; j++) {
            Arrays.fill(path[j], -1);
        }

        for (int k = 0; k < matrix.length; k++) {
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix.length; j++) {
                    if ((matrix[i][k] + matrix[k][j]) < matrix[i][j]) {
                        matrix[i][j] = matrix[i][k] + matrix[k][j];
                        path[i][j] = k;
                    }
                }
            }
        }

        System.out.println(matrix[1][2021]);//10266837
    }

    public static int gcd(int m, int n) {
        if (m % n == 0)
            return n;
        return gcd(n, m % n);
    }

    public static int lcm(int m, int n) {
        return m * n / gcd(m, n);
    }

}

8.蓝桥杯:迷宫(BFS)

image.png

  • 输入:

    01010101001011001001010110010110100100001000101010 00001000100000101010010000100000001001100110100101 01111011010010001000001101001011100011000000010000 01000000001010100011010000101000001010101011001011 00011111000000101000010010100010100000101100000000 11001000110101000010101100011010011010101011110111 00011011010101001001001010000001000101001110000000 10100000101000100110101010111110011000010000111010 00111000001010100001100010000001000101001100001001 11000110100001110010001001010101010101010001101000 00010000100100000101001010101110100010101010000101 11100100101001001000010000010101010100100100010100 00000010000000101011001111010001100000101010100011 10101010011100001000011000010110011110110100001000 10101010100001101010100101000010100000111011101001 10000000101100010000101100101101001011100000000100 10101001000000010100100001000100000100011110101001 00101001010101101001010100011010101101110000110101 11001010000100001100000010100101000001000111000010 00001000110000110101101000000100101001001000011101 10100101000101000000001110110010110101101010100001 00101000010000110101010000100010001001000100010101 10100001000110010001000010101001010101011111010010 00000100101000000110010100101001000001000000000010 11010000001001110111001001000011101001011011101000 00000110100010001000100000001000011101000000110011 10101000101000100010001111100010101001010000001000 10000010100101001010110000000100101010001011101000 00111100001000010000000110111000000001000000001011 10000001100111010111010001000110111010101101111000

  • 输出

    DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDR

1.思路分析

迷宫问题找最短路,首先应该想到的是BFS算法,核心思想就是在图中找到具体的路径,套用BFS算法框架就可以。需要注意的是:题目要求我们输出步数最小的前提下字典序最小的答案。因此我们对起点进行移动的时候优先级——> D-L-R-U

2.代码

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

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

        char[][] arr=new char[30][50];
        for(int i=0;i<30;i++){
            arr[i]=sc.nextLine().toCharArray();
        }
        int[][] dis={
                {1,0},
                {0,-1},
                {0,1},
                {-1,0}
        };
        String[] s={"D","L","R","U"};

        Queue<Node> q=new LinkedList<>();
        q.offer(new Node(0,0,""));
        arr[0][0]='1';
        String res="";
        while(!q.isEmpty()){
            Node cur=q.poll();

            if(cur.x==29&&cur.y==49){
                res=cur.s;
                break;
            }
            for(int i=0;i< dis.length;i++){
                int x=cur.x+dis[i][0];
                int y=cur.y+dis[i][1];
                String curstr= cur.s+s[i];
                if(x>=0&&x<30&&y>=0&&y<50&&arr[x][y]=='0'){
                    q.offer(new Node(x,y,curstr));
                    arr[x][y]='1';
                }
            }
        }
        System.out.println(res);
    }

    public static class Node{
        int x;
        int y;
        String s;

        public Node(int x, int y, String s) {
            this.x = x;
            this.y = y;
            this.s = s;
        }
    }
}