题目
给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。
输出格式
输出一个整数,表示1号点到n号点的最短距离。
如果路径不存在,则输出-1。
数据范围
1≤n,m≤1.5×105,
图中涉及边长均不小于0,且不超过10000。
输入样例:
3 3
1 2 2
2 3 1
1 3 4
输出样例:
3
解法分析
首先我们要知道,这个算法只不过是朴素迪杰斯特拉算法的过程的优化,其核心思路其实是不变的.
首先回忆朴素迪杰斯特拉算法:
- 初始化
- 遍历,找到最小的距离,标记(加入确认集合)
- 遍历,更新所有的距离
- 循环2,3 直到所有点
堆优化的算法就是将这两次遍历优化:
1.初始化堆,初始化链表
2.弹出堆头,标记(加入确认集合)
3.找到堆头相邻的结点,并对其更新,如果结点更新了,就加入堆中,直到堆空
可以看到,找最小距离的步骤我们用弹出堆头的操作来简化;而更新节点的过程,我们用找邻节点的方式简化了遍历的过程
因此,本题我们要用链表来存储图,而链表适用于稀疏图,所以我们说,稀疏图我们要用堆优化的迪杰斯特拉算法来做
源代码
import java.util.*;
class Main{
static int n;
static int N=150010;
static int INF=0x3f3f3f3f;
static int index=0;
static int[] h=new int[N];
static int[] e=new int[N];
static int[] ne=new int[N];
static boolean[] st=new boolean[N];
static int[] dis=new int[N];
static int[] w=new int[N];
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
int m=sc.nextInt();
while(m-->0){
int x=sc.nextInt();
int y=sc.nextInt();
int z=sc.nextInt();
add(x,y,z);
}
Arrays.fill(dis,INF);
System.out.print(djs_2());
}
public static int djs_2(){
PriorityQueue<Pair> pq=new PriorityQueue<Pair>();
pq.add(new Pair(0,1));
dis[1]=0;
while(!pq.isEmpty()){
Pair p=pq.poll();
int d=p.getDistance();
int n=p.getNumber();
if(st[n]) continue; //这句的作用是什么呢?如果有一模一样的两条重边,第二个就不用在根据节点往下找了(找了浪费时间而已,因为下边的距离已经更新过了,所以即使往下走也不会再更新了)
st[n]=true;
for(int i=h[n];i!=0;i=ne[i]){
int j=e[i];
if(dis[j]>dis[n]+w[i]) {//这里的dis[n]其实就是d
dis[j]=dis[n]+w[i];
pq.add(new Pair(dis[j],j));
}
}
}
if(dis[n]==INF) return -1;
return dis[n];
}
public static void add(int x,int y,int z){
e[++index]=y;
ne[index]=h[x];
h[x]=index;
w[index]=z;//为什么用index来存边的权重呢? 因为1个index就对应一个新的边的产生(虽然e[index]表示的是节点,但是这个节点是可以重复的,只有index不能重复),因此用index是最适合的
} //换言之给出了前节点,通过指针我们就能知道后面节点的所有信息
}
class Pair implements Comparable<Pair>{
private int distance;
private int number;
public Pair(int distance,int number){
this.distance=distance;
this.number=number;
}
public int getDistance(){
return this.distance;
}
public int getNumber(){
return this.number;
}
public int compareTo(Pair o){
return Integer.compare(this.distance,o.distance);
}
}
写在最后
- 最大值的选择一定要写:0x3f3f3f3f,否则(0x3f3f3f)不够大
- 邻接表的index是很重要的,因为我们遍历的时候用的就是index,所以我们要得到的其他信息在定义时都要由index来定义/找到
- 重写compareto的时候要注意两点:1. 要写实现了接口comparable 2.要使用泛型 comparable<某某> 否则,传入的参数只能是obj类型的