题解 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; }