The dream of programming thinking C - TT (SPFA algorithm)

Posted by mattclements on Fri, 12 Jun 2020 09:11:02 +0200

subject

TT had a dream this night!
In the dream, TT's wish came true, and he became the leader of meow star! There are n commercial cities on meow star, numbered 1-N, among which city 1 is the capital city of TT.
There are M directional roads on meow star for commercial cities to communicate with each other. But with the prosperity of meow star business, some roads become very crowded. TT is suffering for it, his magic kitten put forward a solution! TT welcomed and issued a new policy for the program.
The specific policies are as follows: mark a positive integer for each commercial city to indicate its prosperity degree. When each meow goes from one commercial city to another along the road, TT will charge them (destination prosperity degree - departure prosperity degree) ^ 3 tax.
TT is going to test whether this policy is reasonable, so he wants to know how much tax is required to pay when he goes from the capital to other cities. If the total amount is less than 3 or can't reach, please type "yes".

Input
Enter t in the first line to indicate that there is a total of T sets of data. (1 <= T <= 50)
For each set of data, enter n in the first row to indicate the number of points. (1 <= N <= 200)
In the second line, input N integers to represent the weights a [i] of points 1 to N. (0 <= a[i] <= 20)
Enter m in the third line to indicate the number of directed roads. (0 <= M <= 100000)
Next row M, each row has two integers A B, indicating that there is A directed path from A to B.
Next, an integer q is given to indicate the number of queries. (0 <= Q <= 100000)
Give a p for each inquiry, which means to find the minimum tax from point 1 to point P.

Output
Output one line for each query. If it is not reachable or the tax is less than 3, then output '?'.

Sample Input
2
5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5
10
1 2 4 4 5 6 7 8 9 10
10
1 2
2 3
3 1
1 4
4 5
5 6
6 7
7 8
8 9
9 10
2
3 10

Sample Output
Case 1:
3
4
Case 2:
?
?

thinking

1, SPFA algorithm
SPFA is an improved Bellman Ford algorithm.
1. Bellman Ford algorithm
Floyd algorithm and Dijkstra algorithm have their limitations
Floyd algorithm solves the problem of multi-source shortest path, which is a waste of solving the problem of single source shortest path.
Dijkstra algorithm can not solve the problem of single source shortest path with negative weight edge.
Bellman Ford algorithm can solve the problem of single source shortest path with negative weight edge.
Assuming that n vertices, m edges and source points are given as s, two one-dimensional arrays are needed to use Bellman Ford algorithm:
dis[i]: the shortest distance from point s to point I
pre[i] = k: point K to pass before point s to point I
The main body of Bellman Ford algorithm:

//initialization
for (int i = 1; i <= n; i++) {
	dis[i] = Inf;
	pre[i] = 0;
}
//Bellman Ford algorithm
dis[s] = 0;
for (int j = 1; j < n; j++) {
	for (int i = 1; i <= m; i++) {
		if (dis[Edges[i].v] > dis[Edges[i].u] + Edges[i].w) {
			dis[Edges[i].v] = dis[Edges[i].u] + Edges[i].w;
			pre[Edges[i].v] = Edges[i].u;
		}
	}
}

The time complexity is O(nm).
2.SPFA algorithm
SPFA is an improved Bellman Ford algorithm. This algorithm adds a one-dimensional array and a queue:
Queue q: store the successfully relaxed points
inq[i] = 0/1: point I in / out of queue q
Main body of SPFA algorithm:

//initialization
for (int i = 1; i <= N; i++) {
	dis[i] = Inf;
	pre[i] = 0;
	inq[i] = 0;
}
//SPFA algorithm
dis[s] = 0;
q.push(s);
inq[s] = 1;
while (!q.empty()) {
	int u = q.front();
	q.pop();
	inq[u] = 0;
	for (int i = Head[u]; i; i = Edges[i].next) {
		int v = Edges[i].to;
		int w = Edges[i].weight;

		if (dis[v] > dis[u] + w) {
			dis[v] = dis[u] + w;
			pre[v] = u;
			if (!inq[v]) {
				q.push(v);
				inq[v] = 1;
			}
		}
	}
}

The time complexity is O(km), k is a constant less than n, but k may be very large in special cases.
3. Improved SPFA algorithm
Although a graph can have a negative weight edge, if it has a negative weight loop, it will fall into a negative weight loop and cannot find the shortest path.
In order to enable the algorithm to judge whether there is a negative weight loop, a one-dimensional array is added to the improved SPFA algorithm:
cnt[i] = k: point I has k edges in the shortest path after joining the queue
When cnt[i] = n, it means that the shortest path after point I joins the queue has n edges, but in fact, the shortest path should not have more than (n-1) edges, so there is a negative weight loop, and the shortest path cannot be found.
The main body of the improved SPFA algorithm:

//initialization
for (int i = 1; i <= N; i++) {
	dis[i] = Inf;
	pre[i] = 0;
	inq[i] = 0;
	cnt[i] = 0;
}
//Improved SPFA algorithm
dis[s] = 0;
inq[s] = 1;
q.push(s);
while (!q.empty()) {
	int u = q.front();
	q.pop();
	inq[u] = 0;
	for (int i = Head[u]; i; i = Edges[i].next) {
		int v = Edges[i].to;
		int w = Edges[i].weight;

		if (dis[v] > dis[u] + w) {
			cnt[v] = cnt[u] + 1;
			if (cnt[v] >= n) {
				//There is a negative loop
			}
			dis[v] = dis[u] + w;
			pre[v] = u;
			if (!inq[v]) {
				q.push(v);
				inq[v] = 1;
			}
		}
	}
}

2, Problem solving
It can be seen from the problem that the graph may have negative weight edge and negative weight loop, so the improved SPFA algorithm is needed.
At the same time, the algorithm needs to be further improved
When cnt[i]=n, it means that all points in point I and its connected components cannot find the shortest path (i.e. not reachable). Then mark these points. If these points are queried later, output "?" directly to reduce the time cost of query.
Further improvement:
Add 1 one dimension array:
visit[i] = false/true: point I reachable / not reachable
When cnt[i]=n, use DFS algorithm to mark the visit of all points in point I and its connected component as true.

code

#include <iostream>
#include <queue>
#include <cstring>
#include <cmath>

#define Inf 10000000
#define MAX_V 100000
#define MAX_E 100000

using namespace std;

int T;
int N;
int a[MAX_V];
int M;
int A, B;
int Q, P;

struct node
{
	int to;
	int weight;
	int next;
}Edges[MAX_E];
int Head[MAX_V];
int tot = 0;

int dis[MAX_V];
int inq[MAX_V];
int cnt[MAX_V];

bool visit[MAX_V];

queue<int> q;

void add(int u, int v, int w)
{
	tot++;
	Edges[tot].to = v;
	Edges[tot].weight = w;
	Edges[tot].next = Head[u];
	Head[u] = tot;
}

void DFS(int v)
{
	for (int i = Head[v]; i; i = Edges[i].next) {
		int to = Edges[i].to;
		if (!visit[to]) {
			visit[to] = true;
			DFS(to);
		}
	}
}
void SPFA(int s)
{
	for (int i = 1; i <= N; i++) {
		dis[i] = Inf;
	}
	dis[s] = 0;
	inq[s] = 1;

	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		inq[u] = 0;
		for (int i = Head[u]; i; i = Edges[i].next) {
			int v = Edges[i].to;
			int w = Edges[i].weight;

			if (visit[v]) continue;

			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				cnt[v] = cnt[u] + 1;
				if (cnt[v] >= N) {
					DFS(v);
					visit[v] = true;
				}
				if (!inq[v]) {
					q.push(v);
					inq[v] = 1;
				}
			}
		}
	}
}

void init()
{
	memset(Head, 0, sizeof(Head));
	memset(inq, 0, sizeof(inq));
	memset(cnt, 0, sizeof(cnt));
	memset(a, 0, sizeof(a));
	memset(visit, false, sizeof(visit));
	tot = 0;
}

int main()
{
	scanf("%d", &T);

	 for (int t = 1; t <= T; t++) {
		init();

		scanf("%d", &N);

		for (int i = 1; i <= N; i++) scanf("%d", &a[i]);
		
		scanf("%d", &M);

		for (int i = 0; i < M; i++) {
			scanf("%d %d", &A, &B);
			int w = pow(a[B] - a[A], 3);
			add(A, B, w);
		}

		SPFA(1);

		scanf("%d", &Q);
		
		printf("Case %d:\n", t);
		for (int i = 0; i < Q; i++) {
			scanf("%d", &P);
			if (dis[P] == Inf || visit[P] || dis[P] < 3) printf("?\n");
			else printf("%d\n", dis[P]);
		}
	 }
}

Topics: less