[Usaco2006 Nov]Roadblocks 次短路

Description

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

Input

* 第1行: 两个整数,N和R,用空格隔开
* 第2..R+1行: 每行包含三个用空格隔开的整数A、B和D,表示存在一条长度为 D(1 <= D <= 5000)的路连接农场A和农场B

Output

* 第1行: 输出一个整数,即从农场1到农场N的第二短路的长度

Sample Input

4 4
1 2 100
2 4 200
2 3 250
3 4 100

Sample Output

450
//最短路:1 -> 2 -> 4 (长度为100+200=300)
第二短路:1 -> 2 -> 3 -> 4 (长度为100+250+100=450)

这是道好题啊,相信大家第一秒看到觉得简单,稍加思考就懵逼了吧,不然也不会看到我这句话。

好了,说正经的:

这道题显然是spfa(虽然范围并不大,但是还是spfa)

其实和普通的spfa没什么两样,就是开一个存次短路的数组,然后在判断最短路径不是更优的时候判断一遍次短路径是否更优,如果是的,替换就行了

这里有一个点不得不说:在找到更优的最短路径的时候,原有的最短路径肯定变成了当前的次短路径(因为我们的目的就是在原有的最短路径上替换一条边,再使替换的这条边最短),所以我们每更新一次最短路,就要连次短路一起更新,精髓好好体会吧。

看代码(就多了一个数组和一个if):

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int sum,n,m,q[5001],last[5001];
bool used[5001];
long long dis[5001],dis2[5001];
struct data
{
    int a,next,v;
}f[200001];
void ins(int u,int v,int w)
{
    sum++;
    f[sum].a=v;
    f[sum].v=w;
    f[sum].next=last[u];
    last[u]=sum;
}
int main()
{
    int t=0,w=1,now,i,u,v,x;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)dis[i]=dis2[i]=1e15;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&u,&v,&x),ins(u,v,x),ins(v,u,x);
    q[0]=1,used[1]=1,dis[1]=0;
    while(t!=w)
    {
        now=q[t++];
        if(t==n)t=0;
        for(i=last[now];i;i=f[i].next)
        {
            if(dis[now]+f[i].v<dis[f[i].a])
            {
                dis2[f[i].a]=dis[f[i].a];//在找到更优的最短路径的时候,原有的最短路径肯定变成了当前的次短路径
                dis[f[i].a]=dis[now]+f[i].v;
                if(!used[f[i].a])
                {
                    used[f[i].a]=1;
                    q[w++]=f[i].a;
                    if(w==n)w=0;
                }
            }
            else if(dis[now]+f[i].v<dis2[f[i].a]&&dis[now]+f[i].v>dis[f[i].a])
            {
                dis2[f[i].a]=dis[now]+f[i].v;
                if(!used[f[i].a])
                {
                    used[f[i].a]=1;
                    q[w++]=f[i].a;
                    if(w==n)w=0;
                }
            }
            if(dis2[now]+f[i].v<dis2[f[i].a])
            {
                dis2[f[i].a]=dis2[now]+f[i].v;
                if(!used[f[i].a])
                {
                    used[f[i].a]=1;
                    q[w++]=f[i].a;
                    if(w==n)w=0;
                }
            }
        }
        used[now]=0;
    }
    if(dis2[n]!=1e15)printf("%d",dis2[n]);
    else printf("-1");
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值