Description
给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权
N<=100000
M<=200000
Input
Output
Sample Input
4 5
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8
Sample Output
12
题解Here!
这个题一眼看去感觉不可做啊。。。
关键就是建图。。。
这是一个比较有技巧的建图方式。
首先考虑暴力点的建图:
常规操作:把每条无向边拆成两条有向边。
把每条边看成一个点,对于两条边$a->b,b->c$:
在这两条边之间连有向边,边权为这两条边的权值的较大值。
新建源点$S$,汇点$T$,$S$向所有从$1$连出去的边连边,所有指向$n$的边向$T$连边。
求$S->T$的最短路即可。
这个方式显然复杂度是$O(m^2)$的,铁定$TLE$。。。
所以考虑骚操作优化。
我们用
类似网络流中补流思想的方法:考虑利用差值来建边。
依然把每条边$x-y$拆成$x->y,y->x$。
枚举每个中转点$x$。
将$x$的出边按权值排序,$x$的每条入边向对应的出边连该边权值的边,$x$的每条出边向第一个比它大的出边连两边权差值的边,$x$的每条出边向第一个比它小的出边连权值为$0$的边。
新建源汇$S,T$,$S$向每条$1$的出边连权值为该边边权的边。
每条$n$的入边向$T$连该边权值的边。
跑$S->T$的最短路即可。
注:
- 此题卡$SPFA$!此题卡$SPFA$!!此题卡$SPFA$!!!重要的事情说三遍!!!所以还是乖乖写堆优化$dijkstra$吧。。。
- 记得与路径长度有关的数组、变量都要开$long\ long$!
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define MAXN 400010
#define MAX (1LL<<60)
using namespace std;
int n,m,c=1,d=2,s,t;
int head[MAXN],h[MAXN],stack[MAXN];
long long path[MAXN];
bool vis[MAXN];
struct Edge{
int next,to;
long long w;
}edge[MAXN],a[MAXN<<2];
struct node{
int x;
long long dis;
bool operator <(const node &p)const{
return dis>p.dis;
}
};
priority_queue<node> q;
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline bool cmp(const int &p,const int &q){
return edge[p].w<edge[q].w;
}
inline int relax(int u,int v,long long w){
if(path[v]>path[u]+w){
path[v]=path[u]+w;
return 1;
}
return 0;
}
inline void add_edge(int u,int v,long long w){
edge[d].to=v;edge[d].w=w;edge[d].next=h[u];h[u]=d++;
edge[d].to=u;edge[d].w=w;edge[d].next=h[v];h[v]=d++;
}
inline void add(int u,int v,long long w){
a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
}
void dijkstra(){
node u,v;
for(int i=s;i<=t;i++){path[i]=MAX;vis[i]=false;}
u.x=s;u.dis=path[s]=0;
q.push(u);
while(!q.empty()){
u=q.top();
q.pop();
if(!vis[u.x]){
vis[u.x]=true;
for(int i=head[u.x];i;i=a[i].next){
v.x=a[i].to;
if(!vis[v.x]){
path[v.x]=min(path[v.x],path[u.x]+a[i].w);
v.dis=u.dis+a[i].w;
q.push(v);
}
}
}
}
}
void build(){
int top;
for(int i=1;i<=n;i++){
top=0;
for(int j=h[i];j;j=edge[j].next)stack[++top]=j;
sort(stack+1,stack+top+1,cmp);
for(int j=1;j<=top;j++){
int now=stack[j],after=stack[j+1];
if(edge[now].to==n)add(now,t,edge[now].w);
if(i==1)add(s,now,edge[now].w);
add(now^1,now,edge[now].w);
if(j<top){
add(now,after,edge[after].w-edge[now].w);
add(after,now,0);
}
}
}
}
void work(){
dijkstra();
printf("%lld\n",path[t]);
}
void init(){
int u,v,w;
n=read();m=read();
s=1;t=((m+1)<<1);
for(int i=1;i<=m;i++){
u=read();v=read();w=read();
add_edge(u,v,w);
}
build();
}
int main(){
init();
work();
return 0;
}