ALGO-15 旅行家的预算

ALGO-15 旅行家的预算

题目

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 D1、汽车油箱的容量 C(以升为单位)、每升汽油能行驶的距离 D2、出发点每升汽油价格 P 和沿途油站数 N(N 可以为零),油站 i 离出发点的距离 Di、每升汽油价格 Pi(i=1,2,……N)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出“No Solution”。

输入格式

第一行为 4 个实数 D1、C、D2、P 与一个非负整数 N;
接下来 N 行,每行两个实数 Di、Pi。

输出格式

如果可以到达目的地,输出一个实数(四舍五入至小数点后两位),表示最小费用;否则输出“No Solution”(不含引号)。

样例输入

\(275.6\ \ \ 11.9\ \ \ 27.4\ \ \ 2.8\ \ \ 2\)
\(102.0\ \ \ 2.9\)
\(220.0\ \ \ 2.2\)

样例输出

26.95

题解

思路

思路1

对应第一个代码答案

  1. 先判断是否能走通,即两个站点之间满油车能不能通过
  2. 找到下一个站点(再满油的范围)
    1. 判断是否有更便宜的站点
      1. 在现有的油量(不加油)可以到达的范围:直接设为下一个站点
      2. 需要加油才能达到:只有在上面的条件为否的情况才设为下一个站点
    2. 没有更便宜的
      1. 看能不能直接到终点,能去就直接去
      2. 从贵的的里找一个最便宜的
  3. 计算当前需要的加油量(费用)
    1. next是终点,加适量
    2. 下一站便宜,加适量
    3. 下一站贵,加满
  4. 循环 2-3 直到终点

思路2

找在最大行驶距离内寻找比当前便宜的加油站,然后判断是否能一次到达,不能的话先加满,然后一个一个判断直到剩下的油量不足到下一个加油站就加油,加适量。

于是有以下 2 situations:

  1. 这个站点 j 就是 pos
    1. 从 pos 就可以走到终点于是我们把油加到刚好到达终点即可
      cost += ((d[i] - d[pos]) / d2 - remain)*p[pos];就得到了最后答案。
      remain 是当前剩余的油
    2. 从 pos 不能一把走到终点
      于是,从当前位置走,走到哪里加油都不够在 pos 这里加油划算。所以加满。
  2. 这个站点 j 是在 pos 后面的某个站点
    1. 当前剩余的油 remain 不够走到 j。于是我们,把油加来刚好能够走到 j 就行了。(因为 j 这里好好便宜!)
    2. 剩余的油 remain 足够,直接开过去就行了。

代码

我自己写的,感觉更有面向对象的风格

import java.util.Scanner;

public class ALGO_15 {
    static double[] d, p; // D 是油站距离,P 是油站价格

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        double D1 = sc.nextDouble();
        double C = sc.nextDouble();
        double D2 = sc.nextDouble();
        double P = sc.nextDouble();
        int N = sc.nextInt();
        d = new double[N + 2];
        p = new double[N + 2];
        for (int i = 1; i < N + 1; i++) {
            d[i] = sc.nextDouble();
            p[i] = sc.nextDouble();
        }
        // 起始站
        d[0] = 0;
        p[0] = P;
        // 终点站
        d[d.length - 1] = D1;
        p[p.length - 1] = 0;
        sc.close();
        double full_run = D2 * C;
        if (reachable(full_run)) {
            System.out.printf("%.2f", getMinCost(C, D2));
        } else {
            System.out.println("No Solution");
        }
    }

    /**
     * 计算出最小费用
     * @param {double} full_run 可行驶最远距离
     * @return {double} 最小费用
     */
    private static double getMinCost(double capacity, double distance) {
        final double full_run = distance * capacity;
        double min_cost = 0;
        double remain = 0; // 剩余油量
        int pos = 0; // 当前位置
        double remain_run;
        while (pos < d.length - 1) {
            remain_run = distance * remain;
            int index = next(pos, full_run, remain_run);
            if (index == d.length - 1) {
                min_cost += (d[index] - d[pos] - remain_run) / distance * p[pos];
            } else if (p[index] > p[pos]) {
                min_cost += p[pos] * (capacity - remain);
                remain = capacity - (d[index] - d[pos]) / distance;
            } else {
                if (d[index] - d[pos] > remain_run) {
                    min_cost += (d[index] - d[pos] - remain_run) / distance * p[pos];
                    remain = 0;
                } else {
                    remain -= (d[index] - d[pos]) / distance;
                }
            }
            pos = index;
        }
        return min_cost;
    }

    /**
     * 找到从 pos+1 到可行使距离的最优站点
     * @param {int} pos 当前位置
     * @param {double} full_run 可行驶最远距离
     * @param {double} remain_run 剩余可行驶距离
     * @return {int} 最优站点 (pos 表示没有找到)
     */
    private static int next(int pos, double full_run, double remain_run) {
        int index = pos;
        for (int i = pos + 1; i < d.length; i++) {
            if (d[i] - d[pos] > full_run) {// 超过可行使距离
                break;
            }
            if (p[i] <= p[index]) {// 找到更便宜的站点
                if (remain_run >= d[i] - d[pos]) {// 剩余油量足够
                    index = i;
                } else {
                    if (index == pos) {
                        index = i;
                    }
                }
            }
        }
        // 没有找到更便宜的站点
        if (index == pos) {
            if (full_run >= d[d.length - 1] - d[pos]) {// 直接到终点
                index = d.length - 1;
            } else {
                // 就在大的之中找一个最小的
                double price = Integer.MAX_VALUE;
                for (int i = pos + 1; i < d.length; i++) {
                    if (d[i] - d[pos] > full_run) {// 超过可行使距离
                        break;
                    }
                    if (p[i] < price) {
                        index = i;
                        price = p[i];
                    }
                }
            }
        }
        return index;
    }

    /**
     * 判断是否可以到达终点
     * @param {double} full_run 可行驶最远距离
     * @return {boolean}
     */
    private static boolean reachable(double full_run) {
        for (int i = 1; i < d.length; i++) {
            if (d[i] - d[i - 1] > full_run) {
                return false;
            }
        }
        return true;
    }
}

网上的答案,略作修改

 import java.util.Scanner;

public class ALGO_15 {
    static double a[]; //位置
    static double b[];//油价
    static double last_oil; //油箱剩余空间
    static double run_c; //剩余油料可以行走的最大距离
    static int best_pos; //形成内最小油料价格的位置
    static double full_run; //邮箱在满了的情况下可以跑多远
    static double use = 0;
    static double d1, c, d2, p;
    static int n;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        d1 = sc.nextDouble();
        c = sc.nextDouble();
        d2 = sc.nextDouble();
        p = sc.nextDouble();
        n = sc.nextInt();
        a = new double[n + 2];
        b = new double[n + 2];
        for (int i = 1; i <= n; i++) {
            a[i] = sc.nextDouble();
            b[i] = sc.nextDouble();
        }
        sc.close();
        a[0] = 0;
        a[n + 1] = d1;
        b[0] = p;
        b[n + 1] = 0;
        last_oil = 0; //初始化邮箱剩余油量
        best_pos = 0; //初始化初始位置
        full_run = c * d2;
        boolean passed = true;
        for (int i = 0; i <= n; i++) { //验证是否可达
            if (a[i + 1] - a[i] > full_run) {
                System.out.println("No Solution");
                passed = false;
                break;
            }
        }
        if (passed) {
            run();
            System.out.println(String.format("%.2f", use));
        }

    }

    public static void run() {

        if (best_pos == n + 1)
            return;
        int flag = 0; //标记在最大行程内有没有找到费用更少的收费站
        run_c = last_oil * d2;
        for (int i = best_pos + 1; i <= n + 1; i++) {
            if (a[i] - a[best_pos] <= full_run) { //必须在full_run范围之内
                if (b[i] <= b[best_pos]) { //如果是更优的选择
                    flag = 1; //标记
                    if (run_c >= a[i] - a[best_pos]) { //如果剩下的油已经够跑了
                        last_oil = (a[i] - a[best_pos]) / d2; //剩余油量更新
                    } else { //如果剩下的油不够跑
                        last_oil = 0; //要恰好跑到价格更低的加油站
                        use += (a[i] - a[best_pos] - run_c) / d2 * b[best_pos];
                    }
                    best_pos = i; //更新最优节点
                    break;
                }
            }
        }

        if (flag == 0) { //没有匹配到最优解
            int better_pos = best_pos + 1;
            for (int i = best_pos + 1; i <= n + 1; i++) { //一定是不可能到n+1的,因为n+1的在上面是一定可以匹配的!因为a[n+1].price是0!
                if (a[i] - a[best_pos] <= full_run) {
                    if (b[i] < b[better_pos]) {
                        better_pos = i;
                    }
                }
            }
            //找到了更优值
            use += (c - last_oil) * b[best_pos]; //一定是加满油最好了!因为在满油的行车范围内都到不了最优的,所以一定要加满油
            last_oil = c - (a[better_pos] - a[best_pos]) / d2; //先更新到达better_pos位置剩下的油料
            best_pos = better_pos;
        }
        run();

    }
}
posted @ 2022-03-25 12:36  morning-start  阅读(64)  评论(0编辑  收藏  举报