[四连测]路径规划(path)----SPFA

原题

题目描述

有n个点,m条无向边,有A,B两个人,初始时刻A在点1,B在点2,他们要走到点n去。A每走一条边,要消耗B单位能量,B每走一条边,要消耗E单位能量。如果A,B相伴走,则只消耗P单位的能量。请问A,B走到点n,最少要消耗多少能量?

输入数据保证1和n,2和n连通。

输入

第一行包含整数B,E,P,N和M,所有的整数都不超过40000,N>=3.

接下来M行,每行两个整数,表示该无向边连接的两个顶点。

输出

一个整数,表示最小要消耗的能量。

样例输入

4 4 5 8 8
1 4
2 3
3 4
4 7
2 5
5 6
6 8
7 8

样例输出

22


思路

首先要读懂题,A自己走孤零零的所以化悲伤为食欲要消耗b能量,B自己走更胆小孤单所以狂吃要消耗e能量,两人一起走…(#)

太麻烦了,你们自己理解吧。

根据题意,A和B迟早会走到一起(至少会在点n相遇)相依为命越好,所以两人的路程可以简单地分为3段:A走的、B走的、AB一起走的。

如果AB在 i 点相遇,那么最短的消耗必定是 (1—i)最短距离xb + (2—i)最短距离xe + (i—n)最短距离xp。

这就需要求两点间的最短距离。但是,这道题是不是用求任意两点间的最短距离的Floyd算法呢?不,不是。用Floyd三重循环O(n^{3})是会超时的。

我们再回顾一下,如果AB在 i 点相遇,那么最短的消耗必定是 (1—i)最短距离xb + (2—i)最短距离xe + (i—n)最短距离xp,只求点 i 到点1、点2和点n的距离。(想到了吗)对了!只用求3次单源点最短路径就可以了。求单源点最短路径有很多方法,这里只用SPFA

逐个枚举节点,依次算出它到点1、点2、点n的最短距离,分别乘以b、e、p,然后比出最小的,就是答案!

代码如下,以理解的就不用看了。


代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#define LL long long
using namespace std;
vector<int>G[40005];
int n,m,i,j,s,e;
LL a,b,c,f1[40005],f2[40005],fn[40005],ans=0x7f7f7f7f;
bool v[40005];
inline void spfa1(){
    queue<int>p;
    p.push(1);
    while(!p.empty()){
        int u=p.front();
        p.pop();
        v[u]=0;
        int si=G[u].size();
        for(int o=0;o<si;o++){
            if(f1[G[u][o]]>f1[u]+1){
                f1[G[u][o]]=f1[u]+1;
                if(!v[G[u][o]])
                    p.push(G[u][o]),v[G[u][o]]=1;
            }
        }
    }
}
inline void spfa2(){
    queue<int>p;
    p.push(2);
    while(!p.empty()){
        int u=p.front();
        p.pop();
        v[u]=0;
        int si=G[u].size();
        for(int o=0;o<si;o++){
            if(f2[G[u][o]]>f2[u]+1){
                f2[G[u][o]]=f2[u]+1;
                if(!v[G[u][o]])
                    p.push(G[u][o]),v[G[u][o]]=1;
            }
        }
    }
}
inline void spfan(){
    queue<int>p;
    p.push(n);
    while(!p.empty()){
        int u=p.front();
        p.pop();
        v[u]=0;
        int si=G[u].size();
        for(int o=0;o<si;o++){
            if(fn[G[u][o]]>fn[u]+1){
                fn[G[u][o]]=fn[u]+1;
                if(!v[G[u][o]])
                    p.push(G[u][o]),v[G[u][o]]=1;
            }
        }
    }
}
int main()
{
    scanf("%lld%lld%lld%d%d",&a,&b,&c,&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&s,&e);
        G[s].push_back(e);
        G[e].push_back(s);
    }
    memset(f1,0x7f,sizeof(f1));
    memset(f2,0x7f,sizeof(f2));
    memset(fn,0x7f,sizeof(fn));
    f1[1]=0;f2[2]=0;fn[n]=0;
    spfa1(),spfa2(),spfan();
    for(i=1;i<=n;i++){
        ans=min(ans,f1[i]*a+f2[i]*b+fn[i]*c);
    }
    printf("%d",ans);
    putchar('\n');
    return 0;
}

有点麻烦,但很好理解。


尾语

欢迎关注我的博客 偶耶(xiong j x)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值