题目大意:要你求出从起点到终点的两条线路,这两条线路不可以有重叠的边,且这两条线路的和最短

解题思路:

解法一:两次SPFA,第一次从起点到终点,第二次从终点到起点,但中间要加一点改变,将走过的最短路径的值变为负数,并删除掉相反的边,以免后面从终点走到起点会重复,为什么要将走过的最短路径的值变为负数呢,因为如果将第一次的最短路径都删掉的话,不一定能取得到最大值,有反例的,




-

UVA - 10806 Dijkstra, Dijkstra. SPFA_最短路径

反例就是这个,图画不好,请见谅,这里解释了为什么不能删除最短路径,第一次的最短路径肯定是这三条权值为1的,如果删掉了,就不会出现有第二条最短路径了,而将其设置为负数的话,就会出现中间那条1走了两次,第二次走的话是减去1的,那样的话,就相当于该路没有被走过,第一次的最短路径也被改了,这样走过的路就变成了,2条权值为10的,两条权值为1的,不包含中间那条权值为1的,距离变成了22


#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

#define INF 0x3f3f3f3f
#define N 110
int dis[N][N], pre[N], d[N];
int n, m;
bool vis[N];

void init() {
	scanf("%d", &m);
	memset(dis, 0x3f, sizeof(dis)); 

	int u, v, c;
	for (int i = 0; i < m; i++) {
		scanf("%d%d%d", &u, &v, &c);
		dis[u][v] = dis[v][u] = c;
	}
}

int spfa(int s) {
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= n; i++)
		d[i]= INF;
	d[s] = 0;
	queue<int> Q;
	Q.push(s);

	while (!Q.empty()) {
		int u = Q.front();
		Q.pop();

		for (int v = 1; v <= n; v++) {
			if (dis[u][v] != INF && d[u] + dis[u][v] < d[v]) {
				d[v] = d[u] + dis[u][v];
				pre[v] = u;
				if (!vis[v]) {
					vis[v] = 1;
					Q.push(v);
				}
			}
		}
		vis[u] = false;
	}
	if (s == 1) {
		int t = n, tt = pre[n];
		while (tt != s) {
			dis[tt][t] = -dis[tt][t];
			dis[t][tt] = INF;
			t = tt;
			tt = pre[tt];
		}
		dis[s][t] = -dis[s][t];
		dis[t][s] = INF;
		return d[n];
	}
	else return d[1];
}

void solve() {
	int ans = 0;
	bool flag = false;
	ans += spfa(1);
	if (ans >= INF) flag = true;
	if (!flag) ans += spfa(n);

	if (flag || ans >= INF) printf("Back to jail\n");
	else printf("%d\n", ans);
}

int main() {
	while (scanf("%d", &n) != EOF && n) {
		init();
		solve();
	}
	return 0;
}



解法二:上面的解法可能比较抽象,因为是参考别人的。现在仔细想一想,其实不用逆向走,还是正向走(从1出发),但还是要改变走过的边的权值

如果你知道网络流的话,这个办法就比较简单了。在网络流里面,有一种边,叫反向边,他能提供给你反悔的机会的

这里我们也一样,对于走过的边,就当成一条流流过去了,所以边的正向权值要变成INF,这样就能表示,该边不通了

而反向的边呢,可能你已经想到了,不错,就是权值变成负数,这样就可以用两次SPFA代替最小费用流了


#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

#define INF 0x3f3f3f3f
#define N 110
int dis[N][N], pre[N], d[N];
int n, m;
bool vis[N];

void init() {
	scanf("%d", &m);
	memset(dis, 0x3f, sizeof(dis)); 

	int u, v, c;
	for (int i = 0; i < m; i++) {
		scanf("%d%d%d", &u, &v, &c);
		dis[u][v] = dis[v][u] = c;
	}
}

int spfa(int s, bool flag) {
	memset(vis, 0, sizeof(vis));
	for (int i = 1; i <= n; i++)
		d[i]= INF;
	d[s] = 0;
	queue<int> Q;
	Q.push(s);

	while (!Q.empty()) {
		int u = Q.front();
		Q.pop();

		for (int v = 1; v <= n; v++) {
			if (dis[u][v] != INF && d[u] + dis[u][v] < d[v]) {
				d[v] = d[u] + dis[u][v];
				pre[v] = u;
				if (!vis[v]) {
					vis[v] = 1;
					Q.push(v);
				}
			}
		}
		vis[u] = false;
	}

	if (!flag) {
		int t = n, tt = pre[n];
		while (tt != s) {
			dis[t][tt] = -dis[tt][t];
			dis[tt][t] = INF;
			t = tt;
			tt = pre[tt];
		}
		dis[t][s] = -dis[s][t];
		dis[s][t] = INF;
		return d[n];
	}
	else return d[n];
}

void solve() {
	int ans = 0;
	bool flag = false;
	ans += spfa(1, false);
	if (ans >= INF) flag = true;
	if (!flag) ans += spfa(1, true);

	if (flag || ans >= INF) printf("Back to jail\n");
	else printf("%d\n", ans);
}

int main() {
	while (scanf("%d", &n) != EOF && n) {
		init();
		solve();
	}
	return 0;
}