Talk about the shortest algorithms (Dijkstra, spfa, floyd) and attach the shortest code implementation of the Blue Bridge Cup

The shortest path is one of the basic problems of getting started with algorithms. I was confused at the beginning of the freshman year. Now the bloggers have a new understanding and understanding of these algorithms after studying algorithm analysis in their junior year, so they are summarized as follows, I hope I can help my future review and those who are just starting to get into the pit algorithm.

First, Dijkstra's algorithm:

Scope of application:

     The weights of the edges are non-negative. If there are edges with negative weights in the graph, Dijkstra's algorithm will fail, and the shortest path calculated may be wrong.

specific process:

    Dijkstra algorithm again:

     Find the closest point from the origin, and then update the distance between the origin and other points through the closest point.

Need to traverse n-1 times.

A bit greedy!

Second, the spfa algorithm:

I originally planned to write the Bellman-ford algorithm again, but I saw the spfa algorithm (O(ke)), that is, the optimized Bellman-ford algorithm (O(ve)). v is the number of nodes. It has been proved that k is far in general It is less than v, so the Bellman-ford algorithm is not explained too much.

Want to understand Bellman-ford algorithm, I found a good blog:

https://blog.csdn.net/niushuai666/article/details/6791765

Can refer to.

Let's talk about the spfa algorithm:

Scope of application: a given graph has negative weight edges. At this time, algorithms like Dijkstra are useless, and the complexity of Bellman-Ford algorithm is too high, SPFA algorithm comes in handy. We agree that there is no negative weight loop in the directed weighted graph G, that is, the shortest path must exist. Of course, we can do a topological sort before executing the algorithm to determine whether there is a negative weight loop, but this is not the focus of our discussion.

Algorithm idea: We use the array d to record the shortest path estimation value of each node, and use the adjacency table to store the graph G. The method we adopt is the dynamic approximation method: set up a first-in first-out queue to save the nodes to be optimized, take out the first node u of the queue each time during optimization, and use the current shortest path estimate of u to leave u The pointed-to node v performs a slack operation. If the shortest path estimation value of the point v is adjusted, and the point v is not in the current queue, the point v is placed at the end of the queue. In this way, nodes are continuously removed from the queue to perform relaxation operations until the queue is empty

The expected time complexity is O(ke), where k is the average number of times all vertices enter the queue, and it can be proved that k is generally less than or equal to 2.

Implementation:

  Create a queue. Initially, there is only the starting point in the queue. Then create a table to record the shortest path from the starting point to all points (the initial value of the table should be assigned a maximum value, and the path from this point to itself is assigned 0). Then perform a slack operation, using some points in the queue as the starting point to refresh to the shortest path of all points, if the refresh is successful and the refreshed point is not in the queue, the point is added to the end of the queue. Repeat until the queue is empty. (The so-called relaxation operation refers to updating the length of the adjacent point from the origin to a through node a!)

Determine whether there is a negative ring:
  If a point enters the queue more than N times, there is a negative ring (SPFA cannot handle graphs with negative rings)

First, establish the shortest path table from the starting point a to the remaining points

First, the source point a enters the queue, when the queue is not empty:
 

1. The head element (a) of the team goes out of the team, and the end points of all sides with a as the starting point are sequentially relaxed (here there are three points b, c, d), and the path table state is:

During the slack, the shortest path estimates of the three points become smaller, and none of these points appear in the queue. These points need to be enqueued. At this time, three new nodes b, c, d are enqueued in the queue.

 

The first element of the team goes out of the team at point b, and the end points of all sides with b as the starting point are sequentially relaxed (here there is only point e). At this time, the path table state is:

In the shortest path table, the shortest path estimate of e has also become smaller, e does not exist in the queue, so e must be enqueued, and the elements in the queue are c, d, e

The head element c goes out of the team, and the end points of all sides with c as the starting point are sequentially relaxed (here there are two points e and f). At this time, the path table state is:

In the shortest path table, the shortest path estimates of e and f become smaller, e exists in the queue, and f does not exist. Therefore, e does not need to be enqueued, and f is enqueued. At this time, the elements in the queue are d, e, f

 The head element d goes out of the team, and the end points of all edges with d as the starting point are slackened in turn (here there is only the point g). At this time, the path table state is:

The leading elements of the team are e, f, g.

Then e points out the right team, e only points to g, and then the shortest path estimate of g does not decrease (relaxation is unsuccessful), no new nodes enter the queue, the elements in the queue are f and g, and the table status is still:

The first element of the team f is out of the team, and the end points of all edges with f as the starting point are sequentially relaxed (here there are three points d, e, and g). At this time, the path table state is:

In the shortest path table, the shortest path estimates of e and g become smaller again, there is no point e in the queue, and e enters the queue. There is a point g in the queue, and g does not need to enter the queue. The elements in the queue are g, e

The first element of the team goes out of the team at point g, and the end points of all sides with g as the starting point are sequentially relaxed (here there is only point b). At this time, the path table state is:

In the shortest path table, the shortest path estimate of b becomes smaller again, there is no point b in the queue, and b enters the queue. At this time, the elements in the queue are e, b


The head element e is out of the team, and the end points of all sides with e as the starting point are sequentially relaxed (here there is only the point g). At this time, the path table state is:

In the shortest path table, the shortest path estimate of g does not change (relaxation is unsuccessful), and the element in the queue is b

The first element of the team goes out of the team at point b, and the slack operation is performed on the end points of all edges with b as the starting point (here there is only the point e). At this time, the path table state is:

In the shortest path table, the shortest path estimate of e has not changed (relaxation is unsuccessful), and the queue is empty at this time

Finally, the shortest path from a to g is 14.

SPFA algorithm theory reprinted blog:

https://blog.csdn.net/maxichu/article/details/45309463

Furthermore, the floyd algorithm:

I wrote in my previous blog. For details, please refer to:

https://blog.csdn.net/Look_star/article/details/87922786

Summary: For the general case of fewer points, you can use the floyd algorithm (O(n^3)), which is simple and rude; for the case of more points, you can use the dijkstra algorithm (O(n^2)). For points There are many, but not many edges, you can use spfa algorithm (O(ke)).

Sample display:

Solve with Dijkstra and spfa algorithm:

                                                                                Algorithm training shortest path  

Problem Description

Given a directed graph with n vertices and m edges (some edge weights may be negative, but it is guaranteed that there are no negative rings). Please calculate the shortest path from point 1 to other points (the vertices are numbered from 1 to n).

Input format

Two integers n, m in the first line.

In the next m lines, each line has three integers u, v, l, which means that there is an edge of length l from u to v.

Output format

There are n-1 rows, and the i-th row represents the shortest path from point 1 to point i+1.

Sample input

3 3
1 2 -1
2 3 -1
3 1 2

Sample output

-1
-2

Data scale and convention

For 10% of the data, n = 2 and m = 2.

For 30% of the data, n <= 5 and m <= 10.

For 100% data, 1 <= n <= 20000, 1 <= m <= 200000, -10000 <= l <= 10000, to ensure that all other vertices can be reached from any vertex.

 

Code display:

Spfa algorithm: algorithm complexity O(ke) k: the number of points entering the queue e the number of edges

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

public class Main{
	//最短路
	final static int INF=Integer.MAX_VALUE;//最大值
	static int n,m;
	static int []u;//始点
	static int []v;//终点
	static int []l;//长度
	static int []d;//始点到i的最短距离
	static int []first;//记录前驱 first[i]是指i作为始点的边
	static int []next;//next[i]是指邻接u[i]点的第i条边前面的那一条邻接u[i]点边,如果没有为-1即循环终止
	
	static boolean[] vis;//标记状态 是否在队列中 在为true
	static Queue<Integer> q=new LinkedList<Integer>();//队列
	
	private static void spfa(int f)//f 原点
	{
		for(int i=0;i<n;i++)//初始化
				d[i]=INF;
		d[f]=0;
		Arrays.fill(vis, false);
		
		q.add(f);
		vis[f]=true;
		
		while(!q.isEmpty())
		{
			int temp=q.poll();//取出第一个节点并将之赋值给temp
			vis[temp]=false;
			for(int i=first[temp];i!=-1;i=next[i])//从后往前遍历,领接表   最后加的边第一个遍历
			{
				//System.out.println(x+" "+ first[x]+" "+i+" "+next[i]);
				//System.out.println(v[i]+" "+d[v[i]]+" "+d[x]+" "+l[i]);
				if(d[v[i]]>d[temp]+l[i])//松弛操作
				{
					d[v[i]]=d[temp]+l[i];
					if(!vis[v[i]])//没在队列
					{
						q.add(v[i]);//加到队列
						vis[v[i]]=true;//状态置为在队列
					}
				}
			}	
		}	
	}
	
	//最短路
	public static void main(String[] args) throws IOException {
		BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); //加快输入,这里如果用scanner会运行超时
		String str = bfr.readLine();
		String[] s = str.split(" ");
		n = Integer.parseInt(s[0]);
		m = Integer.parseInt(s[1]);
		
		u=new int [m+1];//这里本来可以用邻接表的,但是为了简便,直接用了几个数组代替
		v=new int [m+1];
		l=new int [m+1];
		first=new int [n+1];//前驱
		next=new int[m+1];//后继
		d=new int[n+1];//原点到i的最短路径
		vis=new boolean[n+1];
		
		Arrays.fill(first, -1);//全部赋值为-1,表示还没有知道的前驱边
		for(int i=0;i<m;i++)
		{
			str = bfr.readLine();
			s = str.split(" ");
			u[i]=Integer.parseInt(s[0])-1;
			v[i]=Integer.parseInt(s[1])-1;
			l[i]=Integer.parseInt(s[2]);
			next[i]=first[u[i]];//第一条边置-1 后面的依次置前面的边的序号,便于后续从后到前遍历点的邻接边  
			first[u[i]]=i;//前驱为第i条边
		}
		spfa(0);
		for(int i=1;i<n;i++)
			System.out.println(d[i]);
		
	}
}

Full score bingo!

Dijktra algorithm: time complexity O (n^2)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

//只能用一维数组加arrayList 二维数组存储空间肯定炸
class DISTANCE{
	ArrayList<Integer> d=new ArrayList<Integer>();
}//两点间的距离

public class Main{

	final static int maxn=20000;
	final static int INF=0X3f3f3f3f;
	static DISTANCE dis[]=new DISTANCE[maxn+5];
	static boolean vis[]=new boolean[maxn+5];
	
	static void init(int n)
	{
		for(int i=0;i<n;i++)
		{
			dis[i]=new DISTANCE();
			for(int j=0;j<n;j++)
			{
				dis[i].d.add(INF);//初始化两点的距离为无限大
			}
		}
	}
	
	static void Dijkstra(int n)
	{
		vis[0]=true;
		for(int i=0;i<n;i++)//因为有n-1个点,所以要循环n-1次,这里写n次是为了整齐美观
        //第n次可以break,当然这只是循环次数,与具体点的坐标不同
		{
			int p=-1;
			int MIN=INF;
			for(int j=0;j<n;j++)//找到距原点最近的点
			{
				if(!vis[j]&&MIN>dis[0].d.get(j))
				{
					MIN=dis[0].d.get(j);
					p=j;
				}
			}
			if(p==-1) break;//没找到
			vis[p]=true;//已访问过,就不能在访问了
			for(int j=0;j<n;j++)//通过最近的点更新原点到其他点的距离
			{
				if(dis[0].d.get(j)>dis[0].d.get(p)+dis[p].d.get(j))
				{
					dis[0].d.set(j,dis[0].d.get(p)+dis[p].d.get(j));
				}
			}
		}
		
	}
	
	public static void main(String[] args) throws IOException {
		
		BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); 
        //这里如果用scanner会运行超时
		String str = bfr.readLine();
		String[] s = str.split(" ");
		int n = Integer.parseInt(s[0]);
		int m = Integer.parseInt(s[1]);
		
		init(n);

		Arrays.fill(vis,false);
		for(int i=0;i<m;i++)//从0开始
		{
			str = bfr.readLine();
			s = str.split(" ");
			int a=Integer.parseInt(s[0]);
			int b=Integer.parseInt(s[1]);
			int l=Integer.parseInt(s[2]);
			dis[a-1].d.set(b-1,l);
		}
		Dijkstra(n);
		for(int i=1;i<n;i++)
			System.out.println(dis[0].d.get(i));
	}

}

However, for a sufficiently large n, the time complexity of O(n^2) is already intolerable, running overtime, 60 minutes! But the algorithmic idea of ​​this problem is not problematic.

 

Guess you like

Origin blog.csdn.net/Look_star/article/details/88528092