最短路。
给一个无向图,随机删去一条边,问你在所有可能的删边情况下的给定两点间的最短路的最大值。
想象删的这条边如果不在原最短路上,那么无影响,只有在原最短路上删边才可能会造成最短路的值提升。那么就在原最短路上一条边一条边的枚举好了。
这道题说边数M<=N*(N-1)/2
,所以可以认为应该没有重边。对这道题而言,有没有重边非常重要,如果有重边,就不能再用邻接矩阵了(因为邻接矩阵默认每两点间只有一条边,即使加上g[a][b] = min(g[a][b], c)
也没用,它只是多条重边求最小值,它完全不能表达出两点间有多条边的意义),想象最短路上的每条边都有一个复制品,那么答案应该还是原最短路的值,而如果让邻接矩阵处理的话直接把两点间的两条边都删没了(g[x][pre[x]] = INF
)。
所以,如果有重边的话,要用邻接表,最短路算法中的前驱记录该边在边表中的下标,删边的时候根据边表下标来删。
还有,这是个无向图,所有1->N
和N->1
是一样的。
#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;
int g[MAXN][MAXN];
int pre[MAXN];
int d[MAXN];
bool vis[MAXN];
int ans;
void init()
{
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
{
if (i == j) g[i][j] = 0;
else g[i][j] = INF;
}
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;
for (int j = 1; j <= N; j++)
{
if (!vis[j] && g[u][j] != INF)
{
if (d[u] + g[u][j] < d[j])
{
d[j] = d[u] + g[u][j];
if (flag)
pre[j] = u;
}
}
}
}
}
int main()
{
int a, b, c;
for (; ~scanf("%d%d", &N, &M);)
{
init();
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
g[a][b] = g[b][a] = c; // 无向图,这道题说了 M<=N*(N-1)/2,所以应该是不会有重边的,若有重边,绝对不能用邻接矩阵了
}
dijkstra(1, 1);
//ans = max(ans, d[N]); 这句根本没用
int t;
for (int x = N; pre[x] != -1; x = pre[x])
{
t = g[x][pre[x]];
g[x][pre[x]] = g[pre[x]][x] = INF; // 删边
dijkstra(1, 0);
ans = max(ans, d[N]);
g[x][pre[x]] = g[pre[x]][x] = t; // 恢复该边,去删下一条边
}
printf("%d\n", ans);
}
return 0;
}