原题
题目描述
有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三重循环是会超时的。
我们再回顾一下,如果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)