题目大意:要你求出从起点到终点的两条线路,这两条线路不可以有重叠的边,且这两条线路的和最短
解题思路:
解法一:两次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;
}