题意
Graph= { V,E1,E2 },边有边权
现在要从 S 到达T ,期间有一次机会能够通过 E2 中的边,其余时候只能走 E1 中的边,问最短路径,最短路,如果使用了 E2 中的边,还需输出经过边的起点
|V|≤500,|E1|≤1000,|E2|≤1000
解法
分层图最短路:
一般来说,这道题的做法都是枚举通过哪一条边,这种做法可以通过本题,但是对于更大规模的数据来说,例如能够使用多次机会或者 |V|更大 ,那就无法解决了,此时就需要使用分层图最短路
分层图最短路是指在可以进行分层图的图上解决最短路问题,一般模型是:
在图上,有 k 次机会可以0代价通过一条边或者经过另外一个边集之中的边,问起点与终点之间的最短路径
解决这类问题的关键就是怎么建立分层图。一般来说,有以下原则:
①.同一层之间的边直接按输入连接
②.有向边直接从本层的起点连向下一层的终点,无向图的话就从本层起点连向下层终点,从本层终点连向下层起点
然后从第一层的起点出发,进行最短路求解,最后的答案就是每一层的终点的最短路径的最小值
另外,一般来说,分层图问题的边会比较多,使用spfa 效率不高( k∗nm ),所以一般使用堆优化的 Dijkstra ,可以在 nlogn 的时间内解决
复杂度
O(T*2*n*log(2*n))
代码
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define ls 2*k
#define rs 2*k+1
#define Rint register int
#define Lint long long int
using namespace std;
const int INF=0x3f3f3f3f;
const int E=50010;
const int N=5010;
struct Edge
{
int next;
int u,v,w;
}t[E];
struct Head
{
int a[N*10],dis[N*10];
int tail;
void Swap(int x,int y)
{
swap( a[x],a[y] );
swap( dis[x],dis[y] );
}
void insert(Rint k,Rint w)
{
a[++tail]=k,dis[tail]=w;
int tmp=tail;
while( tmp>=2 )
{
if( dis[tmp]<dis[tmp/2] )
{
Swap( tmp,tmp/2 );
tmp=tmp/2;
}
else break ;
}
}
void update(Rint k)
{
int tmp=k;
if( ls<=tail && dis[ls]<dis[tmp] ) tmp=ls;
if( rs<=tail && dis[rs]<dis[tmp] ) tmp=rs;
if( tmp!=k )
{
Swap( tmp,k );
update( tmp );
}
}
void pop()
{
Swap( 1,tail ),tail--;
update( 1 );
}
int top()
{
return a[1];
}
bool empty()
{
return !tail;
}
}q;
int head[N],num;
int dis[N],vis[N];
int Pre[N],s[N];
int n,m,k,S,T;
int cnt;
void add(int u,int v,int w)
{
t[++num]=(Edge){ head[u],u,v,w };
head[u]=num;
}
void work()
{
int tmp;
for(int i=1;i<=2*n;i++) dis[i]=INF,vis[i]=0;
dis[S]=0,q.insert( S,0 );
while( !q.empty() )
{
tmp=q.top(),q.pop();
if( vis[tmp] ) continue ;
vis[tmp]=1;
for(int i=head[tmp],x; i ;i=t[i].next)
{
x=t[i].v;
if( dis[x]>dis[tmp]+t[i].w && !vis[x] )
{
Pre[x]=i;
dis[x]=dis[tmp]+t[i].w;
q.insert( x,dis[x] );
}
}
}
}
int main()
{
int u,v,w,C=0;
while( scanf("%d%d%d",&n,&S,&T)!=EOF )
{
if( ++C>1 ) printf("\n");
cnt=num=0;
memset( Pre,0x0,sizeof Pre );
memset( head,0x0,sizeof head );
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
add( u,v,w ),add( v,u,w );
add( u+n,v+n,w ),add( v+n,u+n,w );
}
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&u,&v,&w);
add( u,v+n,w ),add( v,u+n,w );
}
work();
if( dis[T]<dis[T+n] )
{
u=0,v=dis[T],s[++cnt]=T;
for(int i=Pre[T]; i ;i=Pre[t[i].u])
{
s[++cnt]= t[i].u>n ? t[i].u-n : t[i].u ;
if( t[i].v>n ) u=t[i].u;
}
}
else
{
u=0,v=dis[T+n],s[++cnt]=T;
for(int i=Pre[T+n]; i ;i=Pre[t[i].u])
{
s[++cnt]= t[i].u>n ? t[i].u-n : t[i].u ;
if( t[i].v>n ) u=t[i].u;
}
}
printf("%d",s[cnt]);
for(int i=cnt-1;i>=1;i--) printf(" %d",s[i]);
printf("\n");
if( u ) printf("%d\n",u);
else printf("Ticket Not Used\n");
printf("%d\n",v);
}
return 0;
}