这个题就是严格次短路问题,边是无向边,这里记下三种做法,一是直接dijkstra求,二是dijkstra+A*求,三是两次最短路。
dijkstra:
#include <cstring>
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=100000+10;
int n,m,u,v,w;
int dis[maxn],dis2[maxn];
struct P
{
int to,cost;
bool operator < (const P &a) const
{
return cost>a.cost;
}
};
vector<P> edge[maxn];
void Dijkstra()
{
priority_queue<P> qu;
fill(dis,dis+n+1,INF);
fill(dis2,dis2+n+1,INF);//和dijkstra相比多一个数组存储次短距离。
dis[1]=0;
qu.push(P{1,0});
while(!qu.empty())
{
P x=qu.top();
qu.pop();
int v=x.to,d=x.cost;
if(dis2[v]<d) continue ;//放进队列里面的一定是d<dis2的。这里和刘汝佳的vis数组标记是否访问过剪枝一样
for(int i=0; i<edge[v].size(); i++)
{
P y=edge[v][i];
int d2=d+y.cost;//记录起点经过x到y的距离
if(dis[y.to]>d2)//更新到y的最短距离
{
swap(dis[y.to],d2);
qu.push(P{y.to,dis[y.to]});
}
if(dis2[y.to]>d2 && dis[y.to]<d2)//到y的距离如果大于之前的最短到y的距离小于之前到y的次短距离就更新
{
dis2[y.to]=d2;
qu.push(P{y.to,dis2[y.to]});
}
}
}
printf("%d\n",dis2[n]);
}
int main()
{
while(scanf("%d%d",&n,&m)==2)
{
for(int i=0;i<=n;i++) edge[i].clear();
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&u,&v,&w);
edge[u].push_back(P{v,w});
edge[v].push_back(P{u,w});
}
Dijkstra();
}
return 0;
}
dijkstra+A*:
这里先讲一下思路。首先用dijkstra处理出从n到每个点的最短距离,然后调用A*算法,从1开始处理,每次把相邻的点的边权加上,再存进队列,这样每次存进去的就是1到每个点的距离,然后根据这个距离+当前点到n的最短距离排序。那么第一次n点取出来时就是1到n的最短路,然后继续加相邻边权放入队列,那么第二次处理到n,就是最短路加上一条除了最短路经过的边以外的一条最短边的值,也就是次短距离了。
#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=5000+10;
int n,m,u,v,val;
int dis[maxn];
struct P
{
int to,cost;
bool operator < (const P & a) const
{
return cost>a.cost;
}
};
vector<P> edge[maxn];
void Dijkstra()
{
fill(dis,dis+n+2,INF);
dis[n]=0;
priority_queue<P> qu;
qu.push(P{n,0});
while(!qu.empty())
{
P x=qu.top();
qu.pop();
for(int i=0;i<edge[x.to].size();i++)
{
P y=edge[x.to][i];
if(dis[y.to]>dis[x.to]+y.cost)
{
dis[y.to]=dis[x.to]+y.cost;
qu.push(P{y.to,dis[y.to]});
}
}
}
}
struct node
{
int to,len;
bool operator < (const node & a) const
{
return len+dis[to]>a.len+dis[a.to];
}
};
int A_star()
{//if(dis[1]==INF) return -1;当不存在最短路时要加上,不然会死循环。
priority_queue<node> qu;
qu.push(node{1,0});
int num=0;
while(!qu.empty())//这个地方有点搜索的味道,从1开始不断加上相邻的点的边权,然后放入队列。就是从1开始不断向n拓展
{
node a=qu.top();
qu.pop();
if(a.to==n) num++;
if(num==2) return a.len;
for(int i=0;i<edge[a.to].size();i++)
{
P b=edge[a.to][i];
qu.push(node{b.to,a.len+b.cost});//到b.to这个点,然后1到b.to的距离就是a.to的距离加上ab间边的权值。
}
}
return -1;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<=n;i++)
edge[i].clear();
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&val);
edge[u].push_back(P{v,val});
edge[v].push_back(P{u,val});
}
Dijkstra();
printf("%d\n",A_star());
}
return 0;
}
两次最短:
从起点跑一次最短路,然后再从终点跑一次最短路,然后遍历一遍所有的边,次短路的距离就是其中某条边的权值加上起点到一个点的最短路加上终点到另一个点的最短路
#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#pragma GCC optimize(2)
#define INF 0x3f3f3f3f
using namespace std;
const int N=5000+5;
int n,m,u,v,val;
struct P
{
int to,cost;
};
vector<vector<P> > G;//刚学的二维vector使用,使用前需要resize其大小
int dis1[N],dis2[N],vis[N];
void spfa(int s,int *dis)
{
memset(vis,0,sizeof(vis));
dis[s]=0;
queue<int> qu;
qu.push(s);
while(!qu.empty()){
int U=qu.front();
qu.pop();
vis[U]=0;
for(int i=0;i<G[U].size();i++){
P V=G[U][i];
if(dis[V.to]>dis[U]+V.cost){
dis[V.to]=dis[U]+V.cost;
if(!vis[V.to]){
vis[V.to]=1;
qu.push(V.to);
}
}
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
G.resize(n+1); G.clear();
memset(dis1,INF,sizeof(dis1));
memset(dis2,INF,sizeof(dis2));
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&val);
G[u].push_back(P{v,val});
G[v].push_back(P{u,val});
}
spfa(1,dis1);
spfa(n,dis2);
int ans=INF;
for(int i=1;i<=n;i++){//遍历所有的边
for(int j=0;j<G[i].size();j++){
P now=G[i][j];
int temp=dis1[i]+dis2[now.to]+now.cost;
if(temp>dis1[n] && temp<ans) ans=temp;//严格最小
}
}
printf("%d\n",ans);
}
return 0;
}