洛谷 P9749 [CSP-J 2023] 公路 | ybt 2099:【23CSPJ普及组】公路(road)

【题目链接】

洛谷 P9749 [CSP-J 2023] 公路
ybt 2099:【23CSPJ普及组】公路(road)

【题目考点】

1. 贪心

【解题思路】

已知 v i v_i vi是站点i到站点i+1的距离, a i a_i ai是站点i每升油的价格
s i s_i si表示从站点1到站点i的距离,那么从站点1到站点i+1的距离 s i + 1 s_{i+1} si+1,就是从站点1到站点i的距离 s i s_i si加上从站点i到站点i+1的距离 v i v_i vi,因此有 s i + 1 = s i + v i s_{i+1}=s_i+v_i si+1=si+vi
考虑在第x站加油后,途中经过第y站,一直走到第z站。
如果途中第y站的加油价格 a y a_y ay大于等于第x站的加油价格 a x a_x ax,即满足 a y ≥ a x a_y \ge a_x ayax,为了使加油总价最低,就没必要在第y站加油,只需要在第x站加足够的油一直走到第z站即可。
如果途中第y站的加油价格 a y a_y ay低于第x站的加油价格 a x a_x ax,即满足 a y < a x a_y < a_x ay<ax,那么如果在第x站加的油刚好能走到第y站,而后在第y站足够的加油,走到第z站。这样加油的总价比只在x站加油更低。
因此想到,该问题的贪心选择为:
设第i站后第一个加油价格比第i站低的加油站为第j站(如果没有,则取第n站),在第i站加油量可以使汽车刚好走到第j站。(也就是选择满足 j > i j>i j>i a j < a i a_j<a_i aj<ai的最小的 j j j
第i站到第j站的距离为从第1站到第j站的距离减去第1站到第i站的距离,再减去到第i站时剩下的油可以走的路程 s L e f t sLeft sLeft,也就是 s j − s i − s L e f t s_j-s_{i}-sLeft sjsisLeft。每升油可以走 d d d公里,在第i站时,至少需要 ⌈ ( s j − s i − s L e f t ) / d ⌉ \lceil (s_j-s_i-sLeft)/d \rceil ⌈(sjsisLeft)/d升油才能从第i站走到第j站,就是在第i站需要加的油量。
将该油量乘以每升油可以走的距离 d d d,再减去已经走过的路程 s j − s i − s L e f t s_j-s_i-sLeft sjsisLeft,即为新的 s L e f t sLeft sLeft
将每次加油的量乘以第i站加油的单价,即为在第i站加油花的钱。把所有加油花钱加起来,即为要花的总钱数。

【题解代码】

#include<bits/stdc++.h>
using namespace std;
#define N 100005 
long long n, d, a[N], v[N], s[N], sLeft, sum;//s[i]:从站点1到站点i的距离
long long CeilDiv(long long a, long long b)//求a/b向上取整 
{
	return (a-1)/b+1;//或a%b == 0 ? a/b : a/b+1; 
}
int main()
{
	cin >> n >> d;
	for(int i = 1; i < n; ++i)
	{
		cin >> v[i];
		s[i+1] = s[i]+v[i];//从1到i+1的距离是从1到i的距离加上从i到i+1的距离。 
	}
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	int i = 1;//i:上一次加油的位置 
	for(int j = 1; j <= n; ++j) if(a[i] > a[j] || j == n)//第j站为第i站后第一个加油价格更低的加油站 
	{
		long long dis = s[j]-s[i]-sLeft;//在第i站加油加油要走的路程 
		long long amount = CeilDiv(dis, d);//在第i站的加油量 
		sLeft = amount*d-dis;//从第i站走到第j站还剩下的油可以走的路程 
		sum += amount*a[i];//在第i站加油花的钱 
		i = j;//上一次加油的位置变为j 
	}
	cout << sum;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值