这道题和1595基本一样,但是有两个根本不同:
- 这道题明确说了有重边
- 这道题说不一定连通
对于第一点,用邻接表就好了,在最短路算法中记录前驱,注意,记录的是前驱的边的索引,然后回溯的时候每次删一条边,体会这个循环的写法 for (int x = N; x != 1; x = ve[pre[x] ^ 1].n)
。另外,删边其实还是把边权置为INF
,然后最短路算法里要加上判断当前边权不等于INF
的条件(本来用邻接表是不用加的)。
第二点,不连通输出-1
,说明不保证连通,这个不连通分为两种不连通,一个是什么都不删(原图)也不连通,另一个是删边会不连通。对于第一种,本来就不连通,必须直接输出答案了,因为这时pre[N]
一定等于初始值-1
,根本就没有最短路,若是让他进去了上面那个回溯循环中那还了得?边表下标都成了-1
,简直瞎搞。(找这个bug大概花了半小时)
对于第二种不连通,原本代码是没问题的,但是可以再加快一点速度,若这次删边导致不连通了就可以直接退出循环了。
顺便一说,这道题可以体会SPFA
的威力。
下面这个 234ms,SPFA
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int INF = 1e9;
const int MAXN = 1001;
int N, M, T;
struct Edge
{
int n, w;
};
vector<Edge> ve;
vector<int> v[MAXN];
int pre[MAXN];
int d[MAXN];
bool inq[MAXN];
int ans;
void init()
{
ve.clear();
for (int i = 1; i <= N; i++) v[i].clear();
ans = -1;
memset(pre, -1, sizeof pre);
}
void spfa(int s, int flag)
{
queue<int> q;
memset(inq, 0, sizeof inq);
fill(d + 1, d + N + 1, INF);
q.push(s);
inq[s] = true;
d[s] = 0;
for (; !q.empty();)
{
int u = q.front();
q.pop();
inq[u] = false;
int n, w;
for (int i = 0; i < v[u].size(); i++)
{
n = ve[v[u][i]].n;
w = ve[v[u][i]].w;
if (w == INF) continue; // 本来邻接表是不用这句的
if (d[u] + w < d[n])
{
d[n] = d[u] + w;
if (flag) pre[n] = v[u][i];
if (!inq[n])
{
q.push(n);
inq[n] = true;
}
}
}
}
}
int main()
{
int a, b, c;
scanf("%d", &T);
for (; T--;)
{
scanf("%d%d", &N, &M);
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
ve.push_back(Edge{ b,c });
ve.push_back(Edge{ a,c });
v[a].push_back(i << 1);
v[b].push_back(i << 1 | 1);
}
spfa(1, 1);
if (d[N] == INF) // 非常重要
{
printf("-1\n");
continue;
}
//ans = max(ans, d[N]); 这句话没有一点用处
int t, e1, e2;
for (int x = N; x != 1; x = ve[pre[x] ^ 1].n)
{
e1 = pre[x];
e2 = e1 ^ 1;
t = ve[e1].w;
ve[e1].w = ve[e2].w = INF; // 没法删去,只能临时把边权改成无穷大,相当于这条边没有了
spfa(1, 0);
ans = max(ans, d[N]);
if (ans == INF) break; // 加快一点速度
ve[e1].w = ve[e2].w = t;
}
if (ans == INF)
printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}
下面这个 1388ms,dijkstra
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
using namespace std;
const int INF = 1e9;
const int MAXN = 1001;
int N, M, T;
struct Edge
{
int n, w;
};
vector<Edge> ve;
vector<int> v[MAXN];
int pre[MAXN];
int d[MAXN];
bool vis[MAXN];
int ans;
void init()
{
ve.clear();
for (int i = 1; i <= N; i++) v[i].clear();
ans = -1;
memset(pre, -1, sizeof pre);
}
void dijkstra(int s, int flag)
{
memset(vis, 0, sizeof vis);
fill(d + 1, d + N + 1, INF);
d[s] = 0;
for (int i = 0; i < N; i++)
{
int u = -1, mind = INF;
for (int j = 1; j <= N; j++)
{
if (!vis[j] && d[j] < mind)
{
mind = d[j];
u = j;
}
}
if (u == -1) break;
vis[u] = true;
int n, w;
for (int j = 0; j < v[u].size(); j++)
{
n = ve[v[u][j]].n;
w = ve[v[u][j]].w;
if (w == INF) continue; // 本来邻接表是不用这句的
if (!vis[n] && d[u] + w < d[n])
{
d[n] = d[u] + w;
if (flag)
pre[n] = v[u][j];
}
}
}
}
int main()
{
int a, b, c;
scanf("%d", &T);
for (; T--;)
{
scanf("%d%d", &N, &M);
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
ve.push_back(Edge{ b,c });
ve.push_back(Edge{ a,c });
v[a].push_back(i << 1);
v[b].push_back(i << 1 | 1);
}
dijkstra(1, 1);
if (d[N] == INF)
{
printf("-1\n");
continue;
}
//ans = max(ans, d[N]);
int t, e1, e2;
for (int x = N; x != 1; x = ve[pre[x] ^ 1].n)
{
e1 = pre[x];
e2 = e1 ^ 1;
t = ve[e1].w;
ve[e1].w = ve[e2].w = INF; // 没法删去,只能临时把边权改成无穷大,相当于这条边没有了
dijkstra(1, 0);
ans = max(ans, d[N]);
if (ans == INF) break; // 不加这行2542ms,加上这行1388ms
ve[e1].w = ve[e2].w = t;
}
if (ans == INF)
printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}