CSP历年复赛题-P9749 [CSP-J 2023] 公路
原题链接:https://www.luogu.com.cn/problem/P9749
题意解读:有n个加油点,每个加油点距离、油价不同,每升油走d公里,加油必须整升加,问走完所有点最少加油的金额。
解题思路:
本题有两种思考方式:
1、先加油,看能走到哪里
从1号点开始,考虑最少应该加多少油
显然,应该加的油至少保证刚好走到下一个价钱更低的加油点,如果下一个价钱更低的加油点在上一次加油后有余量可以到达,应该跳过继续往后找价钱低的加油点,直到走到n号点
此方式思考起来比较简单,需要对双指针应用比较熟悉,先给出样例模拟。
样例模拟:
5 4
10 10 10 10
9 8 9 6 5
到第1个点:下一个价格更低的点是2,距离是10,加油量应该是⌈10/4⌉=3,金额3*9=27,可以走到距离3*4=12
到第2个点:下一个价格更低的点是4,距离是30-12 = 18,加油量应该是⌈18/4⌉=5,金额5*8=40,可以走到距离12+5*4=32
到第4个点:下一个价格更低的点是5,距离是40-32=8,加油量应该是⌈8/4⌉=2,金额2*6=12,可以走到终点
总加油金额为:27+40+12=79
编写代码时,需要注意,在寻找下一个加油点时,应该先跳过上一次加油后能到达的所有加油点,然后再找价格更低的加油点。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, d;
int v[N], a[N];
long long s[N]; //s[i]表示到第i个加油点的总距离
int main()
{
cin >> n >> d;
for(int i = 2; i <= n; i++)
{
cin >> v[i];
s[i] = s[i-1] + v[i];
}
for(int i = 1; i <= n; i++) cin >> a[i];
long long ans = 0;
long long last = 0; //加油后能走过的总距离
for(int i = 1; i < n; i++) //枚举每一个加油点
{
int j = i + 1;
while(s[j] <= last && j < n) j++; //跳过加油后能到达的点,由于样例简单,不加这一句也可以AC
while(a[j] >= a[i] && j < n) j++; //找下一个价格更低的加油点,直到最后一个点
long long x = s[j] - last; //下一个加油点总距离 到 上一次加油后能走的总距离 的 差值,即还需要加油才能走过的距离
long long oil = (x + d - 1) / d; //需要加的油量
ans += oil * a[i]; //加油金额
last += oil * d; //加油后能走过的总距离
i = j - 1; //下一个加油点是j
}
cout << ans;
return 0;
}
2、先走,再看油从哪里加
依次走到第2,3,4......n个加油点,看每两个点之间距离需要加多少油
由于每个加油点加油只能加整数升,这样到每个点可能会剩余一些油,还能走一些距离
所以从第i-1点走到第i点时,要计算两点之间需要加多少油,距离不应该是s[i] - s[i-1],而是s[i] - 在i-1点以前加的油能走的总距离
当走到第i点时,知道了相比i-1点需要再加多少油,那么油价是多少呢?
这里是贪心选择,当然选之前出现的加油点中油价最低的!
样例模拟:
5 4
10 10 10 10
9 8 9 6 5
到第1个点:走过的总路程为0,加油金额为0
到第2个点:走过的总路程为10,应该从第1个点加油,加油量为⌈10/4⌉=3,金额3*9=27,加完油后可以走的总路程为3*4=12
到第3个点:走过的总路程为20,上一次加油能走的总路程12,还需要走20-12=8公里,应该从第2个加油点加油,加油量为⌈8/4⌉=2,金额2*8=16,加完油后可以走的总路程为12+2*4=20
到第4个点:走过的总路程为30,上一次加油能走的总路程为20,还需要走30-20=10公里,应该从第2个加油点加油,加油量为⌈10/4⌉=3,金额3*8=24,加完后可以走的总路程为20+3*4=32
到第5个点:走过的总路程为40,上一次加油能走的总路程为32,还需要走40-32=8公里,应该从第4个加油点加油,加油量为⌈8/4⌉=2,金额2*6=12
总加油金额为:27+16+24+12=79
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, d;
int v[N], a[N];
int main()
{
cin >> n >> d;
for(int i = 2; i <= n; i++) cin >> v[i];
for(int i = 1; i <= n; i++) cin >> a[i]; //每个加油站加油的价格
long long ans = 0;
long long s = 0; //到下一个加油站走过的总距离
long long last = 0; //到上一个加油站之前加的油能走的总距离
int minp = INT_MAX; //已走过的加油站最低油价
for(int i = 2; i <= n; i++) //枚举到每一个加油站
{
s += v[i];
minp = min(minp, a[i - 1]); //i号加油站之前的最低油价
long long oil = (s - last + d - 1) / d; //i-1到i号需要加的油,距离除以d向上取整
ans += oil * minp; //在已走过的最低价加油站加油
last += oil * d; //可以走的总距离更新
}
cout << ans;
return 0;
}