弱化版传送门:
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;
}
}