【POJ3255/洛谷2865】[Usaco2006 Nov]路障Roadblocks(次短路)
题目:
POJ3255
洛谷2865
分析:
这道题第一眼看上去有点懵……
不过既然要求次短路,那估计跟最短路有点关系,所以就拿着优先队列优化的Dijkstra乱搞,搞着搞着就通了。
开两个数组:\(dis\)存最短路,\(dis2\)存次短路
在松弛的时候同时更新两个数组,要判断三个条件
(\(u\)是当前考虑的点,\(v\)是与\(u\)有边相连的点,\(d(u,v)\)表示从\(u\)到\(v\)的边长)
1.如果\(dis[v]>dis[u]+d(u,v)\),则更新\(dis[v]\)
2018.11.09Updated: 同时\(dis2[v]\)应成为原\(dis[v]\)。原代码有误,已更新。
2.如果\(dis[v]<dis[u]+d(u,v)\)(不能取等,否则\(dis2[v]\)和\(dis[v]\)可能相等)且\(dis2[v]>dis[u]+d(u,v)\),则更新\(dis2[v]\)
3.如果\(dis2[v]>dis2[u]+d(u,v)\),则更新\(dis2[v]\)(显然,如果2成立,更新后\(dis2[v]=dis[u]+d(u,v)<dis2[u]+d(u,v)\),即3一定不成立)
如果上述三个条件中有任意一个成立,则将\(v\)入队。
还要注意一个地方:因为次短路可能会走“回头路” (即成环) ,所以一个点可以多次进队,不能使用\(vis\)数组判重。
以及起点的\(dis2\)不能初始化为0,因为起点的次短路一定是沿着与其相连的最短的边走出去再回来。
代码:
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
int dis[5010],dis2[5010],n,m;
struct point
{
int id;
int dis;
bool operator<(const point &b)const
{
return dis>b.dis;
}
};
struct edge
{
int to;
int w;
};
priority_queue<point>q;
vector<edge>g[5010];
int main(void)
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a].push_back((edge){b,c});
g[b].push_back((edge){a,c});
}
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(dis2,0x3f3f3f3f,sizeof(dis2));
dis[1]=0;
q.push((point){1,0});
while(!q.empty())
{
int u=q.top().id,d=q.top().dis;
q.pop();
if(d>dis2[u])continue;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i].to,w=g[u][i].w;
bool flag=false;
if(dis[v]>dis[u]+w)
{
dis2[v]=dis[v];
dis[v]=dis[u]+w;flag=true;
}
if(dis[v]<dis[u]+w&&dis2[v]>dis[u]+w)
dis2[v]=dis[u]+w,flag=true;
if(dis2[v]>dis2[u]+w)
dis2[v]=dis2[u]+w,flag=true;
if(flag)q.push((point){v,dis[v]});
}
}
printf("%d",dis2[n]);
return 0;
}