CF1801D/P13534 The way home/回家的路

Problem

著名魔术师博里斯·布迪尼在 X 国旅行,这个国家共有 \(n\) 个城市。不幸的是,他在编号为 \(1\) 的城市遭遇了盗窃。现在,布迪尼需要踏上回家的旅程,目标是回到编号为 \(n\) 的城市。

他打算乘坐飞机返家。全国共有 \(m\) 个航班,第 \(i\) 个航班从城市 \(a_i\) 飞往城市 \(b_i\),票价为 \(s_i\) 卢布。要搭乘某个航班,布迪尼必须身处起点城市 \(a_i\),并且手中至少有 \(s_i\) 卢布(这些钱在登机时会被扣除)。

被盗后,他仅剩 \(p\) 卢布。但他并未气馁!在任意城市 \(i\),他都可以随时举办魔术表演,每场表演能赚 \(w_i\) 卢布。

请帮助布迪尼判断,他是否能够回到家乡。如果可以,求出他至少需要举办多少场表演。

其中\(n\le 800,1\le s_i,w_i,p\le 10^9\)

Solve

这个问题有点类似于最短路,但是代价的计算更为复杂

不难发现,给定情况(余额、位置、下一步目的地),可以直接算出代价,换句话说,不同边之间的代价计算互不干扰,是独立

既然这样,可以参照Dijkstra的底层原理,先找最优点,再去松弛

具体实现在代码中,写一个Dij,再照这个写一个推导答案的函数solve,每次以solve的最优点跑单源最短路,计算从当前点到目标点的代价,进行松弛操作

但是要注意,solve松弛的时候不用按照边来,直接枚举所有能到达的点,因为这个代价计算中含有表演的地点(因为不同地方收入不同),这样才能使所有值得算的情况被统计

具体什么样的情况最优,一定是表演次数最少的情况下,余额最多

Code

Warning:本代码能通过CF,洛谷双倍经验需要修改输入格式

#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,n,m,p,C,a[815];
struct edgeA{
    int to,w;
    bool operator>(const edgeA b) const {
        return w>b.w;
    }
};
struct edgeB{
    int to,pf,res;
    bool operator>(const edgeB b) const {
        if(pf!=b.pf)return pf>b.pf;
        return res<b.res;
    }
};
int dis[815],pf[815],res[815];
bool vis[815],vis2[815];
vector<edgeA> g[815];
inline int f(int x,int y){
    if(x<0)return 0;
    if(x%y==0)return x/y;
    return x/y+1;
}
void dij(int st){
    memset(dis,0x3f,(n+10)*sizeof(long long));
    memset(vis2,0,(n+10)*sizeof(bool));
    priority_queue<edgeA,vector<edgeA>,greater<edgeA> > q;
    dis[st]=0;
    q.push({st,0});
    while(!q.empty()){
        int u=q.top().to,v=q.top().w;
        q.pop();
        if(vis2[u])continue;
        vis2[u]=true;
        for(int i=0;i<g[u].size();i++){
            if(!vis2[g[u][i].to]&&v+g[u][i].w<dis[g[u][i].to]){
                dis[g[u][i].to]=v+g[u][i].w;
                q.push({g[u][i].to,dis[g[u][i].to]});
            }
        }
    }
}
int solve(int st){
    memset(pf,0x3f,(n+10)*sizeof(long long));
    memset(vis,0,(n+10)*sizeof(bool));
    memset(res,0,(n+10)*sizeof(bool));
    priority_queue<edgeB,vector<edgeB>,greater<edgeB> > q;
    pf[st]=0,res[st]=p;
    q.push({st,0,p});
    while(!q.empty()){
        int u=q.top().to,v=q.top().pf,w=q.top().res;
        q.pop();
        if(vis[u])continue;
        vis[u]=true;
        dij(u);
        for(int i=1;i<=n;i++){
            if(vis[i]||dis[i]==dis[0])continue;
            edgeB tmpu={i,v+f(dis[i]-w,a[u]),w+f(dis[i]-w,a[u])*a[u]-dis[i]},tmpv={i,pf[i],res[i]};
            if(tmpv>tmpu){
                pf[i]=tmpu.pf,res[i]=tmpu.res;
                q.push(tmpu);
            }
        }
    }
    return pf[n];
}
signed main(){
    cin>>T;
    while(T--){
        cin>>n>>m>>p;
        for(int i=1;i<=n;i++)cin>>a[i],g[i].clear();
        for(int i=1,u,v,w;i<=m;i++){
            cin>>u>>v>>w;
            g[u].push_back({v,w});
        }
        dij(1);
        if(dis[n]==dis[0])cout<<-1<<endl;
        else cout<<solve(1)<<endl;
    }
    return 0;
}

posted @ 2025-08-03 22:24  一位XXS  阅读(16)  评论(0)    收藏  举报