路障问题
贝茜选择的第二短的路径中,可以包含任何一条在最短路中出现的道路,并且,一条路可以重复走多次。当然咯,第二短路的长度必须严格大于最短路(可能有多条)的长度,但它的长度必须不大于所有除最短路外的路径的长度。
特殊样例
输入:
5 5
1 2 5
2 3 5
3 4 4
4 5 6
1 5 21
输出:
21
输入:
4 5
1 2 100
2 4 200
2 3 100
3 4 100
1 4 301
输出:
301
题意为:找出次短路
思路1
先跑两遍最短路,一个为从1到所有边的,一个为从n到所有边的
记录在d1,d2,数组里//
遍历所有的边(cnt条边),比较 该边(u到v)的权值+d1[u]+d2[v],若该值大于最短路d1[n],则符合题意,但要求找到次短路,就找这些值中的min//
d1[u]为从1到u的最短路,d2[v]为从n到v的最短路//
#include <bits/stdc++.h>
#define M(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
const int N=2e6+7;
const int inf=0x3f3f3f3f;
int f[N],to[N],nex[N],val[N],vis[N],d[N],from[N],pre[N],d1[N],d2[N];
int n,m,cnt,flag=1;
void lsctu(int a,int b,int c)
{
from[++cnt]=a;
to[cnt]=b;
val[cnt]=c;
nex[cnt]=f[a];
f[a]=cnt;
}
priority_queue< pair<int,int> >q;
void dij(int s,int *d)//dij求最短路
{
for(int i=1;i<=n;i++)
d[i]=inf;
memset(vis,0, sizeof(vis));
d[s]=0;
q.push(M(0,s));
while(q.size())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=f[x];i;i=nex[i])
{
int y=to[i];
if(d[y]>d[x]+val[i]){
d[y]=d[x]+val[i];
q.push(M(-d[y],y));}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
lsctu(x,y,z);
lsctu(y,x,z);
}
dij(1,d1);
dij(n,d2);
int ans=inf;
for(int i=1;i<=cnt;i++)
{
if(d1[from[i]]+d2[to[i]]+val[i]>d1[n])//满足大于最短路
ans=min(ans,d1[from[i]]+d2[to[i]]+val[i]);
}
cout<<ans<<endl;
return 0;
}
思路2
基于luogu1186的思路 洛谷1186
先跑一遍最短路,在这一遍中获得pre数组,pre[y]=i 为记录到y点的边的编号//
然后遍历最短路的所有的边,根据1186题的思路,假设该边不能通过,即将其权值改为inf,再进行dij()操作看此时的最短路,一定比d[n]大,才满足题意,找出遍历的到最短路中的最小值即为所求//
需要注意一个点就是,路可以重复走,所以要同时考虑,原来最短路中的边走三次的情况(意思是再走一个来回),因为这中情况可能比那些还小//
#include <bits/stdc++.h>
#define M(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
const int N=2e6+7;
const int inf=0x3f3f3f3f;
int f[N],to[N],nex[N],val[N],vis[N],d[N],from[N],pre[N];
int n,m,cnt,flag=1;
void lsctu(int a,int b,int c)
{
to[++cnt]=b;
val[cnt]=c;
nex[cnt]=f[a];
from[cnt]=a;
f[a]=cnt;
}
priority_queue< pair<int,int> >q;
void dij()
{
for(int i=1;i<=n;i++)
d[i]=inf;
memset(vis,0, sizeof(vis));
d[1]=0;
q.push(M(0,1));
while(q.size())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=f[x];i;i=nex[i])
{
int y=to[i];
if(d[y]>d[x]+val[i]){
d[y]=d[x]+val[i];
if(flag)pre[y]=i;//存最短路的边
q.push(M(-d[y],y));}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
lsctu(x,y,z);
lsctu(y,x,z);
}
dij();
int mmm=d[n];//最短路
flag=0;
int ans=inf;
for(int i=pre[n];i;i=pre[from[i]])
{
int x=val[i];
val[i]=inf;
dij();
if(d[n]>mmm) {
ans = min(ans, d[n]);
}
if(mmm+2*x>mmm)
{
ans = min(ans, mmm + 2 * x);//重复走的情况
}
val[i]=x;//复原该边
}
cout<<ans<<endl;
return 0;
}