图论·最短路·题解【路障·次短路径·Roadblocks】(SPFA)


2019.1.22 记本蒟蒻的第一次博客,还请各位神仙多多指教!
洛谷题目链接
WOJ题目链接

题目

[USACO06NOV]洛谷P2865 路障Roadblocks
WOJ#2240 次短路径Roadblocks

题目描述

贝茜把家搬到了一个小农场,但她常常回到FJ的农场去拜访她的朋友。贝茜很喜欢路边的风景,不想那么快地结束她的旅途,于是她每次回农场,都会选择第二短的路径,而不象我们所习惯的那样,选择最短路。 贝茜所在的乡村有R(1<=R<=100,000)条双向道路,每条路都联结了所有的N(1<=N<=5000)个农场中的某两个。贝茜居住在农场1,她的朋友们居住在农场N(即贝茜每次旅行的目的地)。 贝茜选择的第二短的路径中,可以包含任何一条在最短路中出现的道路,并且,一条路可以重复走多次。当然咯,第二短路的长度必须严格大于最短路(可能有多条)的长度,但它的长度必须不大于所有除最短路外的路径的长度。

输入

第1行:两个以空格分隔的整数:N和R

第2…R + 1行:每行包含三个以空格分隔的整数:A,B和D,用于描述连接交叉点A和B并且长度为D(1≤D≤5000)的道路

输出

第1行:节点1和节点N之间的第二条最短路径的长度

样例

  • 输入样例:
    4 4
    1 2 100
    2 4 200
    2 3 250
    3 4 100
  • 输出样例:
    450

说明

两条路线:1 - > 2 - > 4(长度100 + 200 = 300)和1 - > 2 - > 3 - > 4(长度100 + 250 + 100 = 450)

题意

在一个无向、有权图中,求起点至终点的严格次短路长度。初拿到题后可能不太容易上手毕竟我太菜了 ,但总的说来,思路还是应该非常清晰的。

思路

首先,前向星建图,邻接表存图,应该没太大问题。然后,便容易产生以下三种思路:

  1. 修改最短路。由于第二最短路径至少有一条边和路径 p 1 p1 p1不同,所以可以先枚举 p 1 p1 p1中的每一条边 e 1 e1 e1,将 e 1 e1 e1从图 G G G中删除得到 G ’ G’ G,然后在 G ’ G’ G中求最短路,所有 G ’ G’ G最短路中最短的为第二短路 p 2 p2 p2。这种思路可以采用Dijkstra,很容易想到,但时间复杂度……呵呵。
  2. 同时记录。我们需要同时计算出最短路径和次短路径。大致实现方法: d i s 1 [ v ] = m i n ( d i s 1 [ u ] + g [ u ] [ v ] , d i s 2 [ u ] + g [ u ] [ v ] ) dis1[v]=min(dis1[u]+g[u][v],dis2[u]+g[u][v]) dis1[v]=min(dis1[u]+g[u][v],dis2[u]+g[u][v])。这种思路同样可以采用Dijkstra,不过在此便不再细讲,毕竟不是首推。
  3. 【重点】双向SPFA。首先应该想清楚,点1到点n的次短路,一定存在于最短路的边发生变动后,所得到的路径中,即:次短路=1到x的最短路+ g [ x ] [ y ] g[x][y] g[x][y]+y到n的最短路。由此,我们可以分别以第1点与第n点为起点,预处理出到每一个点的最短路长,并分存于两个 d i s [ ] dis[ ] dis[]数组中,此时 d i s [ n ] dis[n] dis[n](或是 d i s [ 1 ] dis[1] dis[1])便是最短路长。而后,枚举每一条边作为中间边 ( x , y ) (x,y) (x,y)或者 ( y , x ) (y,x) (y,x),如果加起来长度刚好等于最短路长度则跳过,否则更新。大致实现方法:如果起点到一个端点的最短路+另一个端点到终点的最短路+长度≠最短路,则和答案比较,保存最小值,采用SPFA实现。以下代码并不是最快最小的,但在下自认为思路还是很清晰的。

代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e4+10;
const int maxm=2e5+10;
const int inf=0x3f3f3f;
int n,m,cnt,minn,ans=inf;//minn为最小值,ans为答案(次小值)
int dis[maxn][2],vis[maxn],first[maxm];//dis数组分两层处理,第0层起点为1,第1层起点为n
struct edge {int u,v,w,nxt;};
edge e[maxm];
inline int read()
{
	int x=0,f=1;
	char c;
	for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
	if(c=='-') {f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
	return x*f;
}//读优(讲究)
inline void add(int u,int v,int w)
{
	e[++cnt].u=u;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].nxt=first[u];
	first[u]=cnt;
}//前向星
void spfa(int s,int k)
{
	for(int j=1;j<=n;j++) dis[j][k]=inf; 
	memset(vis,0,sizeof(vis));
	queue<int> q;
	dis[s][k]=0;
	vis[s]=1;
	q.push(s);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		vis[x]=0;
		for(int i=first[x];i;i=e[i].nxt)
		{
			int y=e[i].v;
			int z=e[i].w;
			if(dis[y][k]>dis[x][k]+z)
			{
				dis[y][k]=dis[x][k]+z;
				if(!vis[y])
				{
					q.push(y);
					vis[y]=1;
				}
			}
		}
	}
}//SPFA模板稍作修改
int main()
{
	n=read();m=read();
	int u,v,w;
	for(int i=1;i<=m;i++)
	{
		u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
	}
	spfa(1,0);
	spfa(n,1);//双向SPFA
	int p;
	minn=dis[n][0];//存储最短路
	for(int i=1;i<=cnt;i++)//枚举经过边
	{
 		p=dis[e[i].u][0]+e[i].w+dis[e[i].v][1];//次短路=1到x的最短路+g[x][y]+y到n的最短路
		if(p>minn) ans=min(ans,p);//更新
	}
	cout<<ans;
	return 0;
}

Emm……写完博客已经是2019.1.23了。请问写完后觉得此题简单得丢人是正常现象么?
总之,希望能对未来的自己有所帮助吧,小可不才!

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值