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)
题意
在一个无向、有权图中,求起点至终点的严格次短路长度。初拿到题后可能不太容易上手毕竟我太菜了 ,但总的说来,思路还是应该非常清晰的。
思路
首先,前向星建图,邻接表存图,应该没太大问题。然后,便容易产生以下三种思路:
- 修改最短路。由于第二最短路径至少有一条边和路径 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,很容易想到,但时间复杂度……呵呵。
- 同时记录。我们需要同时计算出最短路径和次短路径。大致实现方法: 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,不过在此便不再细讲,毕竟不是首推。
- 【重点】双向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了。请问写完后觉得此题简单得丢人是正常现象么?
总之,希望能对未来的自己有所帮助吧,小可不才!