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;
			
			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);
		}
	}
}

poj1511
大意:求1号点到其他点的最短路 + 其他点到1号点的最短
方法:

先正常建图(因为这题点太多了,所以用链式前向星存储边),使用spfa,求一次dis的和	 	  
 再反向建图,求dis的和,加在一起就好了。
烦人的地方就在于: 第一dis[]数组用long ,结果也用long ,第二  inf一定切记要足够大
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值