[CF1801D] The way home
[CF1801D] The way home
Description
一个人在一张有向图的 \(1\) 号结点,他要去到 \(n\) 结点。每条边 \((a_i,b_i)\) 有边权 \(s_i\),表示走过这条边需要花 \(s_i\) 元。这个人一开始有 \(p\) 元,到了一个点 \(u\),他可以进行若干次演出,每次演出收获 \(w_u\) 元。问到达 \(n\) 的最小演出次数,若无解输出 -1
。
Solution
首先我们先考虑该题这个人的表演策略,即如果回家的路径确定了,那么我们直接考虑表演的策略。显然有他在某一个点 \(x\) 表演够恰好回到 \(n\) 结点数,而后直接走最小花费的路径回到 \(n\) 结点。接着我们考虑他是如何来到 \(x\) 的,必然有在另一点 \(x'\) 表演够恰好来到 \(x\)。所以我们如果如果已经确定走的路径,那么他的表演策略是确定的。
于是我们考虑先对 \(n\) 个点求出最短路,再用类似贪心的方式求出下次表演的地点。我们考虑在这部分求算中,有两个变量影响,一个是 \(cost_i\) 即到达 \(i\) 点需要表演的次数,和 \(res_i\) 即此时剩余的钱数。显然我们在表演次数取到最小时,使得剩余钱数最多,所以我们考虑使用优先队列维护这样形如堆优化迪杰斯特拉的算法。每次我们取出当前点 \(u\) 时,我们枚举下一个停靠点 \(i\)。则我们可以计算出演唱次数 \(times\)。而后我们比较两种方法优劣性并更新答案,所有答案即为 \(cost_n\)。
考虑用类似堆优化迪杰斯特拉实现。注意取整需要考虑被除数为负数的情况,建议手写。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T;
int n,m,p;
const int N=800+7;
vector<pair<int,int>> G[N];
bool vis[N],_vis[N];
int cost[N],res[N];
int dis[N],b[N];
struct node{
int dis,id;
friend bool operator < (node a,node b){
return a.dis>b.dis;
}
};
struct node2{
int x,cost,res;
friend bool operator < (node2 a,node2 b){
if(a.cost!=b.cost) return a.cost>b.cost;
return a.res<b.res;
}
};
void Dj(int s){
memset(_vis,0,sizeof _vis);
priority_queue<node> q;
memset(dis,80,sizeof dis);
dis[s]=0;
q.push({0,s});
while(!q.empty()){
int u=q.top().id;
q.pop();
if(_vis[u]) continue;
_vis[u]=1;
for(auto i:G[u]){
int k=i.first,w=i.second;
if(dis[k]>dis[u]+w){
dis[k]=dis[u]+w;
q.push({dis[k],k});
}
}
}
}
//int _ceil(int x,int y){
// if(x<0)return 0;
// if(x%y==0)return x/y;
// return x/y+1;
//}
int _ceil(int x,int y){if(x<0) return 0;return (x+y-1)/y;}
void Get_ans(){
memset(vis,0,sizeof vis);
priority_queue<node2> q;
cost[1]=0,res[1]=p;
q.push((node2){1,cost[1],res[1]});
while(!q.empty()){
int u=q.top().x;
q.pop();
if(vis[u]) continue;
vis[u]=1;
Dj(u);
for(int i=1;i<=n;i++){
if(!vis[i]){
// int times=(int)ceil((dis[i]-res[u])*1.0/b[u]);
int times=_ceil(dis[i]-res[u],b[u]);
// printf("times:%lld\n",times);
node2 tmp={i,cost[u]+times,res[u]+times*b[u]-dis[i]};
node2 bf={i,cost[i],res[i]};
if(bf<tmp){
q.push(tmp);
cost[i]=tmp.cost;
res[i]=tmp.res;
}
}
}
}
}
void solve(){
scanf("%lld%lld%lld",&n,&m,&p);
for(int i=1;i<=n;i++){
G[i].clear();
scanf("%lld",&b[i]);
vis[i]=0;cost[i]=0x3f3f3f3f3f3f3f3f;res[i]=0;
}
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
G[u].push_back(make_pair(v,w));
}
Dj(1);
// for(int i=1;i<=n;i++) printf("%d ",dis[i]);
if(dis[n]==dis[0]) return printf("-1\n"),void();
Get_ans();
printf("%lld\n",cost[n]);
}
signed main(){
cin>>T;
while(T--) solve();
return 0;
}