p3371 单源最短路径(弱化版)-java题解-最短路

弱化版传送门: 

P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


题目背景
本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通过。

题目描述
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入格式
第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。

接下来 m 行每行包含三个整数 u,v,w,表示一条 u→v 的,长度为 w 的边。

输出格式
输出一行 n 个整数,第 i 个表示 s 到第 i 个点的最短路径,若不能到达则输出 231−1。

输入输出样例
输入

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出

0 2 4 3

说明/提示
【数据范围】
对于 20%20% 的数据:1≤n≤5,1≤m≤15;
对于 40%40% 的数据:1≤n≤100,1≤m≤104
对于 70%70% 的数据:1≤n≤1000,1≤m≤105
对于 100%100% 的数据:1≤n≤104 1≤m≤5×105 ,保证数据随机。


思路:

        最近刚学最短路,目前只接触了dijkstra算法,所以这道题用的是dijkstra。网上大多是c++题解,而这道题(弱化版)和它的标准版(标准版的传送门在最上面)都十分毒瘤(搞得我头疼),但很少有人给出比较好的java题解。dijkstra具体原理我就不细说了,大家可以去看看c++版本的,质量好的一找一大堆。建议大家把原理弄懂后再来看我的代码。

        这里先给出70分代码:

import java.util.*;
import java.io.*;

public class Main {
    static int[][] graph;//邻接矩阵
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer st = new StreamTokenizer(br);
        st.nextToken();int n=(int)st.nval;
        st.nextToken();int m=(int)st.nval;
        st.nextToken();int s=(int)st.nval;
        graph=new int[n][n];
        for(int i=0;i<graph.length;i++){
            Arrays.fill(graph[i],0x7fffffff);
        }
        for(int i=0;i<m;i++){
            st.nextToken();int u=(int)st.nval;
            st.nextToken();int v=(int)st.nval;
            st.nextToken();int w=(int)st.nval;
            if(graph[u-1][v-1]!=0) {
            	graph[u-1][v-1]=graph[u-1][v-1]>w?w:graph[u-1][v-1];
            } else {
            	graph[u-1][v-1]=w;

            }
        }
        for(int i=0;i<n;i++) graph[i][i]=0;
        int[] ans = dijkstra(s-1);
        for(int i : ans){
            System.out.print(i+" ");
        }
    }
    static int[] dijkstra(int start){
        boolean[] vis=new boolean[graph.length];
        for(int i=0;i<graph.length;i++){
            if(i==start) vis[i]=true;
            else vis[i]=false;
        }
        int[] dis=new int[graph.length];
        for(int i=0;i<graph.length;i++){
            dis[i]=graph[start][i];
        }
        for(int i=0;i<graph.length;i++){
            int max=0x3f3f3f3f;
            int index=0;
            for(int j=0;j<graph.length;j++){
                if(!vis[j]&&dis[j]<max){
                    max=dis[j];
                    index=j;
                }
            }
            vis[index]=true;
            for(int j=0;j<graph.length;j++){
                if(dis[j]>dis[index]+graph[index][j]){
                    dis[j]=dis[index]+graph[index][j];
                }
            }
        }
        return dis;
    }
}

        上面这段代码用的是邻接矩阵,由于二维数组开得过大,爆内存了。在这种情况下,我们就得换种写法了,c++有结构体,java有类?但不要忘了,java的类比c++结构体大很多,同样是创建node类型的数组(node[] arr = new node[n+1]),java做这道题还是会爆掉。那么这时候,邻接表就派上用场了!邻接矩阵作为二维数组,如果两个点没有直接连接的边,我们就假设有一条长度为无穷大的边直接连接那两个点。而邻接表不一样,没有直接相连的两个点,我们不需要存储信息,我们只需要存储每个点的单源能到达的点的那条边,节省了很多空间。

        同时,由于可能存在两个点之间有很多直接相连的单源边,那么我们只需要存储最短的那条边,毕竟我们求得是最短路(又节省了很多空间),该如何处理呢?重写一下equals就好了。

        下面给AC代码:

import java.util.*;
import java.io.*;
class node{
    int u;
    int v;
    int w;
    public node(int u,int v,int w){
        this.u=u;
        this.v=v;
        this.w=w;
    }
    @Override
    public boolean equals(Object o){
        if(this==o)return true;
        if(!(o instanceof node)) return false;
        node n=(node)o;
        return this.u==n.u&&this.v==n.v;    
        //这里只需要用上u与v,不能用上w,为了后面输入数据的时候不会出错
    }
    @Override
    public int hashCode(){
        return Objects.hash(u,v);
    }
}
public class Main {
    static int n,m,s;
    static List<List<node>> graph=new ArrayList<>();
    public static void main(String[] args) throws IOException{
        //快速输入,没毛病
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer st = new StreamTokenizer(br);
        st.nextToken();n=(int)st.nval;
        st.nextToken();m=(int)st.nval;
        st.nextToken();s=(int)st.nval;
        for(int i=0;i<=n;i++) graph.add(new ArrayList<node>());
        for(int i=0;i<m;i++){
            st.nextToken();int u=(int)st.nval;
            st.nextToken();int v=(int)st.nval;
            st.nextToken();int w=(int)st.nval;
            node n=new node(u,v,w);
            List<node> list=graph.get(u);
            if(list.contains(n)){    //如果u,v之间已经存在u指向v的边
                if(list.get(list.indexOf(n)).w>w){//如果新的边的长度要小于已经存在的边的长度
                    list.remove(n);               //那么我们就除去老的边,用新的边替换
                    list.add(n);
                }
            } else {
                list.add(n);         //如果u,v之间不存在u指向v的边,直接将n添加进来
            }
        }
        int[] dis=dijkstra(s);
        for(int i=1;i<=n;i++){
            System.out.print(dis[i]+" ");
        }
    }
    static int[] dijkstra(int start){
        boolean[] vis=new boolean[n+1];    
        Arrays.fill(vis,false);    //初始化
        vis[start]=true;           //起始点默认已经走过了
        int[] dis=new int[n+1];
        Arrays.fill(dis,(1<<31)-1);//不要忘了先初始化最大值,题目要求(1<<31)-1
        List<node> list=graph.get(start);
        for(node n : list){
            dis[n.v]=n.w;
        }
        dis[start]=0;              //起始点到起始点的距离为0
        for(int i=1;i<=n;i++){     //经典模板
            int min=(1<<31)-1;
            int index=0;
            for(int j=1;j<=n;j++){
                if(!vis[j]&&dis[j]<min){
                    min=dis[j];
                    index=j;
                }
            }
            vis[index]=true;
            List<node> arr=graph.get(index);
            for(node n : arr){
                if(dis[n.v]>dis[n.u]+n.w){
                    dis[n.v]=dis[n.u]+n.w;
                }
            }
        }
        return dis;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡左家陇分卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值