题解 P4874 【[USACO14DEC] Piggyback_Silver 背负式运输(银)】

此题其实比较水的。。。

首先我们来分析题目,我们假设,贝茜和艾尔西在i点相遇后贝西开始扛艾尔西,那么代价为多少呢?

显然,代价=贝茜到i点的最小代价+艾尔西到i点的最小代价+贝茜和艾尔西一起到n点的最小代价。

我们分开解决,首先贝茜到i点的最小代价=走的最少步数*b,而走的步数,我们跑一遍以spfa(1)就可以轻松求出来了!

同理。艾尔西,我们跑spfa(2)就可以求出,而共同走到n,我们跑spfa(n)即可~

意思是,我们先跑三遍spfa:spfa(1),spfa(2),spfa(n),分别求出1到i的步数,设为dis[i][1].2到i的最小步数,设为dis[i][2],n到i的最小步数,设为dis[i][3].

那么我们就可以知道,在i点集合后再背负到n的代价为:dis[i][1]b+dis[i][2]e+dis[i][3]*p;

所以我们只需要O(n)枚举集合点i,然后O(1)计算取最小就好了

至于如果p>b+e怎么办,其实此时最优方案就是各走各的,就相当于在n点集合。

以下为代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long//三年oi一场空,不开long long见祖宗.
const int N=4e4+1,inf=1e17;
struct node{
    int v,nex;
}t[N<<1];
int len,las[N];
int dis[N][4],n;
bool vis[N];
inline void add(int u,int v){//加双向边
    len++;
    t[len].v=v;
    t[len].nex=las[u],las[u]=len;
    len++;
    t[len].v=u;
    t[len].nex=las[v],las[v]=len;
}
inline void spfa(int z,int y){//spfa,当然,它死了...
    queue<int>s;
    s.push(z);
    dis[z][y]=0;
    while(!s.empty()){
        int x=s.front();
        s.pop();
        vis[x]=0;
        for(int i=las[x];i;i=t[i].nex){
            int v=t[i].v;
            if(dis[v][y]>dis[x][y]+1){
                dis[v][y]=dis[x][y]+1;
                if(!vis[v]){
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
}
main(){
    memset(dis,0x3f3f,sizeof(dis));//初始化
    int b,e,p,m;
    scanf("%lld%lld%lld%lld%lld",&b,&e,&p,&n,&m);
    for(int i=1;i<=m;++i){
        int u,v;
        scanf("%lld%lld",&u,&v);
        add(u,v);
    }
    spfa(1,1),spfa(2,2),spfa(n,3);//跑spfa,因为空间只需要3*n,所以用此方法。。。
    long long ans=inf;
    for(int i=1;i<=n;++i){//枚举集合点
        long long now=1LL*b*dis[i][1]+1LL*e*dis[i][2]+1LL*p*dis[i][3];//计算最小代价
        ans=min(ans,now);
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ThinkofBlank/p/10166922.html