1999年NOIP普及组旅行家的预算(洛谷P1016):贪心算法实战指南

深度学习
• 阅读 18

1999年NOIP普及组旅行家的预算(洛谷P1016):贪心算法实战指南

一、问题背景

旅行家的预算是NOIP1999普及组的经典题目,考察贪心算法在实际问题中的应用。题目描述一位旅行家需要从起点到终点,途中有若干个加油站,每个加油站油价不同,要求在有限油箱容量下规划最优加油策略,使总花费最少。

二、数据结构设计

struct Station {
    double distance;  // 加油站距离起点的位置
    double price;     // 该加油站的油价
};

使用结构体存储每个加油站的信息,便于后续处理。

三、核心算法思路

  1. 加油站预处理:将起点和终点也视为特殊加油站,并按距离排序
  2. 可达性检查:计算满油能行驶的最大距离(C*D2)
  3. 贪心策略
    • 优先寻找当前可达范围内比当前站更便宜的加油站
    • 若无更便宜加油站,则在当前站加满油
    • 每次只加到达下一站所需的油量

四、完整代码解析

#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;

struct Station {
    double distance;
    double price;
};

int main() {
    double D1, C, D2, P;
    int N;
    cin >> D1 >> C >> D2 >> P >> N;

    vector<Station> stations(N+2);
    stations[0] = {0, P}; // 起点加油站
    for (int i = 1; i <= N; ++i) {
        cin >> stations[i].distance >> stations[i].price;
    }
    stations[N+1] = {D1, 0}; // 终点设为虚拟加油站

    // 按距离排序加油站
    sort(stations.begin(), stations.end(), [](const Station& a, const Station& b) {
        return a.distance < b.distance;
    });

    double max_distance = C * D2; // 满油能行驶的最大距离
    double current_gas = 0; // 当前油量
    double total_cost = 0; // 总花费
    int current_station = 0; // 当前所在加油站

    // 检查是否能到达第一个加油站
    if (stations[1].distance > max_distance) {
        cout << "No Solution" << endl;
        return 0;
    }

    while (current_station <= N) {
        // 寻找下一个比当前便宜的加油站
        int next_station = current_station + 1;
        double min_price = stations[current_station].price;
        int cheapest_station = -1;

        // 在可达范围内寻找最便宜的加油站
        while (next_station <= N+1 && 
               stations[next_station].distance - stations[current_station].distance <= max_distance) {
            if (stations[next_station].price < min_price) {
                min_price = stations[next_station].price;
                cheapest_station = next_station;
                break; // 找到第一个更便宜的加油站就停止
            }
            next_station++;
        }

        if (cheapest_station != -1) {
            // 计算需要加的油量
            double distance = stations[cheapest_station].distance - stations[current_station].distance;
            double needed_gas = distance / D2 - current_gas;
            if (needed_gas > 0) {
                total_cost += needed_gas * stations[current_station].price;
                current_gas = 0;
            } else {
                current_gas -= distance / D2;
            }
            current_station = cheapest_station;
        } else {
            // 没有更便宜的加油站,加满油到最远的加油站
            if (stations[current_station].distance + max_distance >= D1) {
                // 可以直接到达终点
                double distance = D1 - stations[current_station].distance;
                double needed_gas = distance / D2 - current_gas;
                if (needed_gas > 0) {
                    total_cost += needed_gas * stations[current_station].price;
                }
                cout << fixed << setprecision(2) << total_cost << endl;
                return 0;
            }

            // 检查是否能到达下一个加油站
            next_station = current_station + 1;
            if (next_station > N || 
                stations[next_station].distance - stations[current_station].distance > max_distance) {
                cout << "No Solution" << endl;
                return 0;
            }

            // 加满油到下一个加油站
            total_cost += (C - current_gas) * stations[current_station].price;
            current_gas = C - (stations[next_station].distance - stations[current_station].distance) / D2;
            current_station = next_station;
        }
    }

    cout << fixed << setprecision(2) << total_cost << endl;
    return 0;
}

五、常见错误与优化

  1. 可达性检查:必须首先检查能否到达第一个加油站
  2. 浮点数精度:使用double类型并控制输出精度
  3. 边界条件:特别注意终点处理和无解情况

来源:竞赛学习

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
01背包问题(动态规划求解)
这两天c的习题开始不考察c了,开始考察动态规划问题,唉,没学过动态规划算法来编这题目真是一把辛酸泪,下面给出题目(题目来源:郭玮老师的mooc)2:CharmBracelet查看提交统计提问总时间限制:1000ms内存限制:65536kB描述Bessiehasgonetothemall’s
贾蔷 贾蔷
1个月前
洛谷P11228地图探险题解(CSP-J 2024真题)
一、题目重述给定n×m的二维矩阵表示探险地图,每个格子可能是:平地('.')障碍物('')起点('S')终点('E')求从起点到终点的最短路径步数,无法到达则输出1。二、核心算法:BFS广度优先搜索选择原因:BFS是解决无权图最短路径问题的最优方案,时间复
贾蔷 贾蔷
4星期前
牛客12576题全解析:动态规划+质因数分解解决跳跃问题
一、题目解读牛客12576题是一道经典的算法题,要求给定起点N和终点M,求解从N到M的最少跳跃次数。题目考察的核心在于路径优化与动态规划思想,需结合数论中的质因数分解技巧,通过合理设计算法降低时间复杂度,避免暴力枚举的指数级耗时。二、解题思路采用“动态规划
贾蔷 贾蔷
4星期前
2023年GESP六级小杨握手问题(洛谷B3874):Fenwick树求解逆序对的代码解析
一、题目解读“小杨的握手问题”源自2023年GESP六级考试(对应洛谷题目B3874)。题目描述为:给定一个长度为N的排列,每次将当前数与之前未访问过的数握手,求总共握手次数。本质上是求排列中逆序对的个数,即统计每个数右侧比它小的元素数量。需设计高效算法在
深度学习 深度学习
4星期前
洛谷1111题解:基于Kruskal算法与并查集的最小生成树实现
一、题目解读洛谷1111题是一道经典的图论问题,要求构建一个无向图的最小生成树,并输出其最大边权值。题目核心在于通过给定的边集合,找到连接所有节点的最小权值子集,同时保证无环。这通常涉及最小生成树算法(如Kruskal)的应用,需要高效处理边权重与节点连通
深度学习 深度学习
4星期前
2024蓝桥杯省赛B组前缀总分(洛谷P12124)解题思路与代码详解
一、题目解读2024年蓝桥杯省B组题目“前缀总分”(对应洛谷P12124)要求计算给定字符串集合中,所有前缀的最长公共前缀(LCP)的总分,并找出通过移动字符位置后可能获得的最大总分。题目考察字符串处理与动态规划能力,需高效计算LCP并优化得分策略。二、解
贾蔷 贾蔷
4星期前
2023年GESP六级题解:洛谷P10108闯关游戏动态规划解法详解
一、题目解读本文针对2023年GESP六级题目“闯关游戏”(洛谷P10108)进行详细解析。题目要求玩家通过不同关卡路径选择,计算从起点到终点的最大得分。关卡间存在跳跃规则,需结合动态规划思想设计高效算法,最终输出最优得分。二、解题思路采用动态规划(Dyn
深度学习 深度学习
4星期前
洛谷P2034题解:动态规划+单调队列优化求解最大K段子段和问题
一、题目解读洛谷P2034题目要求给定一个长度为n的整数数组,将其分成不超过k段,求各段和的最大值。该问题属于经典动态规划问题的扩展,需结合优化技巧高效求解。二、解题思路采用动态规划单调队列优化的策略。核心思想是定义状态dp
深度学习 深度学习
3星期前
2012年NOIP提高组「借教室」(洛谷P1083)解题思路与二分查找优化代码解析
一、题目解读本题为2012年NOIP提高组中的「借教室」问题(洛谷P1083),要求处理教室借用订单的分配问题。给定n天每天可用教室数量r和m个订单(订单包含需求教室数d、开始日期s、结束日期t),判断能否通过删除部分订单使所有订单满足教室容量限制。若能,
贾蔷 贾蔷
1星期前
CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用
一、题目解读2019年的“纪念品”问题(对应P5662)要求玩家在T天内通过买卖纪念品最大化金币收益。每天可交易N种商品,需计算最优策略下的最终金币数。题目强调思维与资源分配优化,是中的经典题型。二、解题思路核心思路为“动态规划”。每天将当前商品价格与次