上午学校里组织测试碰到了这道题于是兴趣一起写了这篇Blog。
愉悦地在加长路径的时候加错了一条边,然而还是对了9个点……感叹自己的RP……果然经常%dalao还是有用的。
这个题本身很明显就能看出是最短路问题,因为N≤250所以用迪杰斯特拉算法应该会更快一些,当然SPFA本身时间复杂度就低而且本题M≤25000也可以用。
为了防止玩脱加了个优先队列优化(堆优化)。用邻接表存储的图,但是给我在找路径的时候徒增了许多麻烦……因为这个题要求找出加草堆之后的最大差值,所以我们可以得出草堆只能加在原图点1–>N的最短路径上,否则的话对最短路没有影响。所以我存储下来最短路径,找出FJ所走的边后一个个乘2再跑一遍dij,这比直接一条条边乘2跑算法要优化不少。
因为是无向图,所以要开2*M的数组用来存边,正着反着各存一遍。
下面是我的代码。
#include<cstdio>
#include<queue>
using namespace std;
int u[50050],v[50050],w[50050],f[255],t[50050],cnt,d[255],n,m,rlt,rbt,pre[255];
struct E{
int n,d;
}e;
bool operator <(E i,E j){//重载<号方便建堆(优先队列)优化
return i.d>j.d?1:i.d==j.d?i.n>j.n?1:0:0;
}
priority_queue<E>q;
queue<int>road;//存储最短路径
void add(int U,int V,int W){cnt++;
u[cnt]=U;v[cnt]=V;w[cnt]=W;
t[cnt]=f[U],f[U]=cnt;
}//邻接表添加边
void reset(){int i,j;
for(i=2;i<=n;i++)d[i]=0x3f3f3f3f;
d[1]=0;
}//重置最短距离
int main(){int i,j,U,V,W,s,h;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d%d%d",&U,&V,&W);
add(U,V,W),add(V,U,W);
}reset();
e.n=1;e.d=0;q.push(e);//堆优化dij
while(!q.empty()){h=q.top().n;
s=f[h],q.pop();
while(s){
if(d[v[s]]>d[h]+w[s]){
d[v[s]]=d[h]+w[s];
pre[v[s]]=h;
e.n=v[s],e.d=d[v[s]];
q.push(e);
}s=t[s];
}
}rbt=d[n];s=n;//求出最短路径
while(s){h=f[pre[s]];
while(h){
if(v[h]==s){
road.push(h);
break;
}
h=t[h];
}s=pre[s];
}
while(!road.empty()){reset();//根据路径一个个路径修改求最大差值
if(road.front()&1)
w[road.front()+1]*=2,w[road.front()]*=2;
else
w[road.front()]*=2,w[road.front()-1]*=2;
e.n=1;e.d=0;q.push(e);//依然是dij
while(!q.empty()){h=q.top().n;
s=f[h],q.pop();
while(s){
if(d[v[s]]>d[h]+w[s]){
d[v[s]]=d[h]+w[s];
e.n=v[s],e.d=d[v[s]];
q.push(e);
}s=t[s];
}
}if(d[n]-rbt>rlt)rlt=d[n]-rbt;
if(road.front()&1)
w[road.front()]/=2,w[road.front()+1]/=2;
else
w[road.front()]/=2,w[road.front()-1]/=2;
road.pop();
}printf("%d",rlt);
return 0;
}