题目描述
破了魔法阵后,亮亮进入了一座迷宫。这座迷宫叫做“梦境迷宫”,亮亮只有走出这座迷宫,才能从睡梦中醒来。
梦境迷宫可以用无向图来表示。它共有 n 个点和 m 条双向道路,每条道路都有边权,表示通过这条道路所需的时间, 且每条道路可以多次经过。 亮亮位于一号点, 而出口则是 n 号点。原本,亮亮该找到一条最短路,快速冲出迷宫,然而,梦境迷宫的特殊之处在于,如果沿着最短路到达出口,亮亮就会永远陷入梦境。因此,亮亮必须寻找一条次短路。次短路的长度须严格大于最短路(可以有多条)的长度,同时又不大于所有除最短路外的道路的长度。
你的任务,就是编写一个程序,帮助亮亮找到通向出口的次短路。
分析
题意简单来说就是求一个无向联通带权图中1到n的严格次短路长度。这里有两种算法。
第一种,用两边最短路,分别从1,n跑一遍,在枚举每条边,让1到n的路径中必须包含这条边,再与1到n的最短路作比较,并更新最优值。
第二种,用一遍Spfa,用两个数组dis,dis1分别记录到这个点的最短路与次短路,在松弛的时候,先用最短路更新最短路,不行就用最短路更新次短路,然后再用次短路更新次短路。最后dis1[n]即为答案。
代码
第一种:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int N=5005,M=100005;
struct Edge {
int to,next;
long long weight;
}e[M*2];
int n,m;
int h[N],cnt,k;
long long d[2][N],vis[N],ans;
void add(int x,int y,long long z) {
e[++cnt]=(Edge){y,h[x],z};
h[x]=cnt;
}
void spfa(int v0) {
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(v0);
vis[v0]=1;
d[k][v0]=0;
while (!q.empty()) {
int x=q.front();
q.pop();
vis[x]=0;
for (int i=h[x];i;i=e[i].next) {
int y=e[i].to;
if (d[k][x]+e[i].weight<d[k][y]) {
d[k][y]=d[k][x]+e[i].weight;
if (!vis[y]) {
vis[y]=1;
q.push(y);
}
}
}
}
++k;
}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) {
int a,b;
long long c;
scanf("%d%d%lld",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
for (int i=1;i<=n;i++)
d[0][i]=d[1][i]=(1ULL<<63)-1LL;
spfa(1);
spfa(n);
ans=(1ULL<<63)-1LL;
for (int i=1;i<=n;i++) {
for (int j=h[i];j;j=e[j].next) {
int y=e[j].to;
long long p=d[0][i]+e[j].weight+d[1][y];
if (p>d[0][n]) ans=min(ans,p);
}
}
printf("%lld",ans);
return 0;
}
第二种:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int N=5005,M=100005;
struct Edge {
int to,next;
long long weight;
}e[M*2];
int n,m,vis[N];
int h[N],cnt;
long long d[2][N];
void add(int x,int y,long long z) {
e[++cnt]=(Edge){y,h[x],z};
h[x]=cnt;
}
void spfa() {
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++) d[0][i]=d[1][i]=(1ULL<<62)-1;
queue<int> q;
q.push(1);
vis[1]=1;
d[0][1]=0;
while (!q.empty()) {
int x=q.front();
q.pop();
vis[x]=0;
for (int i=h[x];i;i=e[i].next) {
int y=e[i].to;
if (d[0][x]+e[i].weight<d[0][y]) {
d[1][y]=d[0][y];
d[0][y]=d[0][x]+e[i].weight;
if (!vis[y]) {
vis[y]=1;
q.push(y);
}
} else if (d[0][x]+e[i].weight>d[0][y]&&d[0][x]+e[i].weight<d[1][y]) {
d[1][y]=d[0][x]+e[i].weight;
if (!vis[y]) {
vis[y]=1;
q.push(y);
}
}
if (d[1][x]+e[i].weight<d[1][y]) {
d[1][y]=d[1][x]+e[i].weight;
if (!vis[y]) {
vis[y]=1;
q.push(y);
}
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) {
int a,b;
long long c;
scanf("%d%d%lld",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
spfa();
printf("%lld",d[1][n]);
return 0;
}