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

 

posted @ 2024-06-20 15:06  五月江城  阅读(323)  评论(0编辑  收藏  举报