spfa随笔

本博客并无技术参考价值,若想学习SPFA算法,请找寻其他博客

Dijkstra算法,较之spfa,对于我来说,是相对容易接受的,因为前者使用二维数组进行图的存储,但凡题目中给出边的信息也好,点的信息也好,都可以将图的信息保存在一个map数组中。 之后在进行 Djk算法实现的时候,基本流程也很简洁。

1 .初始化dis 数组
2. for n 次 ,每次找出 没处理过的那些点之中 最短的那个
3.      再在其中 for n次 ,进行没处理过的那些点的距离的更新
我刚入门这玩意,就觉的写起来很顺手,思路也很通畅。

但是如果点很多很多,请问你该怎么办,你的map数组将很大,可能超内存。
而SPFA算法,则是注重于边的存储,他按顺序存储一个一个边,一般把边弄成个结构体,而我们使用一个结构体数组按顺序来存储所有的边。 但是如何让让这些数组里的边产生关系?
我们就可以使用 链式前向星来实现。
何谓链式前向星? 简而言之就是:

边数组  存储 边
每个边都有个起点和终点 ,按上到下按顺序来看,如果我这个边,是这个起点的第一个边,那么这个边的next是-1
如果我这个边,不是起点上的第一个边,那就很简单了,我的next指向上一个边。

看图:

在这里插入图片描述
对于这个图,我们这样描述:

1  2       0号边

2 3		  1号边

3 4		2号边

1 3		3号边

4 1		4号边

1 5		5号边

4 5		6号边

如果使用链式前向星处理,有以下几个东西。

  1. 存储边的数组: edges[] , 之中的元素一般是个边,也就是结构体
  2. edges[ x ].next : 也就是某个边的next是谁? 可能是-1,因为起点上就这一个,也可能是 4, 意思是,x边的的起点上 还连了一个4号边。
  3. head[x] : x 边的起点 上 ,不是有很多边嘛, 这个head[x]就是,这么多边中,最后面的那一个
举例子:
上面的0号边, 3号边,  5号边 都是起点1 
因此  : edges[0].next = -1
	 	edges[3].next=0
	 	edges[5].next=3
还有:head[1] = 5

拿hdu2066为例子:
hdu2066
就是个基本的前向星+spfa, 因为最大节点是1000,实际上也可以用djk来做,因为1000*1000 也不是不能接受的数据规模。

import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main{

	
	static int n;
	static int m;
	static private final int inf = 0x3f3f3f;
	static edge[] edges;
	static int[] head;
	static int cont;
	static int[] near;
	static int[] want;
	static int[] dis;
	static int[] in;
	static int[] nums; //记录一个点入队次数
	
	static class  edge{ //一个边的结构体
		int begin,end,value,next;
	}

	
	static void add(int begin,int end,int value) {
		edges[cont] = new edge();
		edges[cont].end = end;
		edges[cont].value = value;
		edges[cont].next = head[begin];
		head[begin]= cont++;
	}
	static  int spfa(int begin ) {
		Queue<Integer> q=  new LinkedList<Integer>();
		in = new int [10005];
		nums = new int [10005];
		//初始化dis数组为inf
		dis = new int[10006];  for(int i=0;i<10006;i++)dis[i]=inf;
		
		dis[begin]=0;
		in[begin]=1;
		nums[begin]++;
		q.add(begin);

		while(!q.isEmpty()) {
			int top  = q.poll();
			in[top]=0;
			//一定要铭记,edges中 存的是边!   edges 里面是边!!!!!!
			//head存的是数字!!!!
			for(int i=head[top];i>=0;i=edges[i].next) { //链式前向星遍历
				//System.out.println(i);
				int end = edges[i].end; //这个边的终点
				if(dis[end] > dis[top]+edges[i].value) {
					dis[end] = dis[top]+edges[i].value;
					if(in[end]==0) {
						q.add(end);
						in[end]=1;
						nums[end]++;
						//如果这个点加入的次数超过边数,证明存在负圈
						if(nums[end]>m) 
							return -1;
					}
				}
			}
		}
		return 1;
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner (new BufferedInputStream(System.in));
		while(sc.hasNext()) {
			m=sc.nextInt();
			n=sc.nextInt();
			int e = sc.nextInt();
			
			edges = new edge[10005];
			near = new int[n+2];
			want  =new int[e+2];
			cont =0;
			head = new int[10006];  for(int i=0;i<10006;i++)head[i]=-1;
			
			for(int i=0;i<m;i++) {
				int a= sc.nextInt();int b=sc.nextInt();int c = sc.nextInt();
				add(a,b,c);
				add(b,a,c);
			}
			for(int i=0;i<n;i++) 
				near[i]=sc.nextInt();
			for(int i=0;i<e;i++) 
				want[i] =sc.nextInt();
			
			
			int res = inf;
			for(int i=0;i<n;i++) {
				spfa(near[i]);
				for(int j=0;j<e;j++) {
					res = Math.min(res, dis[want[j]]);
				}
			}
			System.out.println(res);
		}
	}
}

发布了35 篇原创文章 · 获赞 5 · 访问量 2409

猜你喜欢

转载自blog.csdn.net/qq_24884193/article/details/104354726