Extended Traffic LightOJ - 1074 最短路径 (SPFA+负环判断+dfs 好题)

  • Extended Traffic

题目链接:https://vjudge.net/problem/26791/origin

Dhaka city is getting crowded and noisy day by day. Certain roads always remain blocked in congestion. In order to convince people avoid shortest routes, and hence the crowded roads, to reach destination, the city authority has made a new plan. Each junction of the city is marked with a positive integer (≤ 20) denoting the busyness of the junction. Whenever someone goes from one junction (the source junction) to another (the destination junction), the city authority gets the amount (busyness of destination - busyness of source)3 (that means the cube of the difference) from the traveler. The authority has appointed you to find out the minimum total amount that can be earned when someone intelligent goes from a certain junction (the zero point) to several others.

Input

Input starts with an integer T (≤ 50), denoting the number of test cases.

Each case contains a blank line and an integer n (1 < n ≤ 200) denoting the number of junctions. The next line contains nintegers denoting the busyness of the junctions from 1 to n respectively. The next line contains an integer m, the number of roads in the city. Each of the next m lines (one for each road) contains two junction-numbers (source, destination) that the corresponding road connects (all roads are unidirectional). The next line contains the integer q, the number of queries. The next q lines each contain a destination junction-number. There can be at most one direct road from a junction to another junction.

Output

For each case, print the case number in a single line. Then print q lines, one for each query, each containing the minimum total earning when one travels from junction 1 (the zero point) to the given junction. However, for the queries that gives total earning less than 3, or if the destination is not reachable from the zero point, then print a '?'.

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

 

2

10 10

1

1 2

1

2

Sample Output

Case 1:

3

4

Case 2:

?

题意:给你n个城市的拥挤度,m条单向道路,每条道路上的通行时间为(终点城市的拥挤度-起点城市的拥挤度)^3,

问你从起点城市1到目的城市x所用的最短时间是多少,无法求出或者最短是时间小于3则输出"?",否则输出最短时间。

思路:此题求最短路,由于存在负环(通行时间为差的3次方可能出现负数),所以考虑用Bellman-Ford算法或者SPFA算法。

此题用Bellman-Ford算法会超时(一开始个人认为可能是计算时无关的边太多的原因导致超时),所以后来我使用spfa算法来求解。

 

超时代码(Bellman-Ford算法):

注:只将算法部分改成spfa算法仍然会超时,问题具体解决方法下面会讲。

3008ms

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#define ll long long
const int INF = 0x3f3f3f3f;
const int MAXN = 100000 + 5;
using namespace std;
int n, m;
int dist[210];
//int visit[MAXN];
//int map[210][210];
int arr[210];
bool Bellman_Ford();
struct edge {
	int st, end;
	int cost;
}e[MAXN];
int main() {
	int t;
	int k = 1;
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &arr[i]);
		}
		scanf("%d", &m);
		for (int i = 1; i <= m; ++i) {
			scanf("%d%d", &e[i].st, &e[i].end);
			e[i].cost = pow(arr[e[i].end] - arr[e[i].st], 3);
		}
		int q;
		scanf("%d", &q);
		printf("Case %d:\n", k);
		for (int i = 1; i <= q; ++i) {
			int a;
			scanf("%d", &a);
			if (!Bellman_Ford())
				printf("?\n");
			else {
				if (dist[a] >= 3)
					printf("%d\n", dist[a]);
				else
					printf("?\n");
			}
		}
		k++;
	}
	return 0;
}

bool Bellman_Ford() {
	for (int i = 1; i <= n; ++i) {
		dist[i] = INF;
	}
	dist[1] = 0;
	for (int j = 1; j < n; ++j) {
		for (int i = 1; i <= m; ++i) {
			int x = e[i].st, y = e[i].end;
			if (dist[y] > dist[x] + e[i].cost) {
				dist[y] = dist[x] + e[i].cost;
			}
		}
	}
	for (int i = 1; i <= m; ++i) {
		int x = e[i].st, y = e[i].end;
		if (dist[y] > dist[x] + e[i].cost)
			return false;
	}
	return true;
}

但是实际上没有那怎么简单。此题不能每次询问都做一次spfa(可能之前bellman-ford也是这个问题),不然还是会超时。那怎么办呢?实际上我们需要对点进行预处理,找出存在于负环中的点和那些点能到达的点(因为这些点之后也可能进入负环当中,所以也要排除),这就要用到dfs算法来找点(只在出现负环的时候用dfs算法),找到后新开一个数组标记一下就行了。这样一次spfa就能够解决之后的所有询问。还要注意通过dfs以后找到的点就不用再在spfa算法中在做判断,不然也是会超时的,因为重复了很多的步骤。

此题实际上是将最短路和搜索结合在一起,只用一次spfa就将问题解决,相当于通过搜索来做进一步的优化,降低时间复杂度,且效果是非常明显的。

AC代码(SPFA+dfs):

73ms

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#define ll long long
const int INF = 0x3f3f3f3f;
const int MAXN = 100000 + 5;
using namespace std;
int n, m;
int dist[210];
int cnt[210]; //用于记录入队次数
int visit[210]; // 用于记录点是否入队
int judge[210];
int arr[210];
int map[210][210];
void dfs(int x);
void SPFA(int s);

int main() {
	int t;
	int k = 1;
	scanf("%d", &t);
	while (t--) {
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				if (i == j)
					map[i][j] = 0;
				else
					map[i][j] = INF;
			}
		}
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &arr[i]);
		}
		scanf("%d", &m);
		for (int i = 1; i <= m; ++i) {
			int a, b;
			scanf("%d%d", &a, &b);
			int c = arr[b] - arr[a];
			map[a][b] = c*c*c;
		}
		int q;
		SPFA(1);
		scanf("%d", &q);
		printf("Case %d:\n", k);
		for (int i = 1; i <= q; ++i) {
			int a;
			scanf("%d", &a);
			if (dist[a] < 3 || judge[a]|| dist[a] == INF)
				printf("?\n");
			else
				printf("%d\n", dist[a]);
		}
		k++;
	}
	return 0;
}

void dfs(int x) {
	judge[x] = 1;
	for (int i = 1; i <= n; ++i) {
		if (map[x][i] != INF) {
			if (!judge[i])
				dfs(i);
		}
	}
}

void SPFA(int s) {
	queue<int> q;
	memset(visit, 0, sizeof(visit));
	memset(cnt, 0, sizeof(cnt));
	memset(judge, 0, sizeof(judge));
	for (int i = 1; i <= n; ++i) {
		dist[i] = INF;
	}
	dist[s] = 0;
	visit[s] = 1;
	q.push(s);
	cnt[s]++;
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		visit[u] = 0;
		for (int i = 1; i <= n; ++i) {
			if (judge[i]) //这句话很重要 原来没有加 结果一直超时。在负环中的点就不需要再进行判断了,因为dfs已经搜过了
				continue;
			if (map[u][i] != INF) {
				if (dist[i] > dist[u] + map[u][i]) {
					dist[i] = dist[u] + map[u][i];
					if (!visit[i]) {
						q.push(i);
						cnt[i]++;
						if (cnt[i] >= n) {
							dfs(i);
						}
					}
				}
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_43821265/article/details/86771742