洛谷——P1016 旅行家的预算

本文介绍了一种使用贪心算法解决特定加油路径问题的方法。该问题要求找到从起点到终点的最佳加油策略,以最小化总花费。文章详细解释了解决方案背后的逻辑,并提供了实现该算法的C++代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接

这是一道比较简单的贪心题目。这个题的解法如下:

如果把起点定为第0个加油站,终点定为第n+1个加油站。
从第0个点开始,驱车至能到达的最远的那个加油站,在第0个点把油加满。这其中会遇到三种情况:
1.中间有一个比出发点油价更便宜的加油站。
2.中间的油价都比出发点高。
3.不能到达下一个加油站。

很容易知道,第三种情况下是不能够到达的。如果是第一种情况,只需要在初始点加油至能够到达那个加油站即可,把剩余的油卖出(相当于加油时没有加满),然后加满低价油。如果是第二种情况,则需要在这里所有经过的的加油站中选择一个油价最便宜的一个加油站,把油加满(不卖油)。在第一或第二种的情况下,以选择的加油站作为起点,重复操作。

这里解释一下原因:第一种情况很容易理解,从出发点到油价更低点,中间的油价均高于出发点。用价格更低的油必然比用出发点的油好。第二种情况,是因为在出发点的油价比路程中遇到的油价都要便宜,但是如果消耗完油量后依然不能到达终点,这种情况下就需要加油了。显然,用这之中最便宜的加油站补充油显然是最合适的。

这里可能会有一个疑问:如果先是遇到第二种情况,后遇到第一种情况,卖油时的价格怎么算?这里油价应该是选用第二种情况下选的加油站的油价。因为遇到第一种情况时,这个加油站油价会比第二种情况遇到的加油站便宜,这样一定会超出第二种情况的最长路程。所以之前加的油已经消耗完了,剩余的是第二种情况时加的油。

代码如下:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;

double D1,C, D2, P;
int N;
// dis数组储存的是第i个加油站到第i-1个加油站的距离
double dis[510];
// pdis数组储存的是第i个加油站到第0个加油站的距离
double pdis[510];
// pri数组储存的是每个加油站的价格
double pri[510];
// s储存的是满油量的最长路程
int s;
// Gas结构体储存的是加油站的位置和价格
struct Gas {
	int index;
	double price;
}gas;
// gas储存的是每一次行进中油价最低的加油站(不考虑出发点)

int main()
{
	ios_base::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> D1 >> C >> D2 >> P >> N;
	for (int i = 1; i <= N; i++) {
		cin >> dis[i];
		cin >> pri[i];
		pdis[i] = dis[i];
	}
	// 第N+1个加油站到第0个加油站的距离就是D1
	pdis[N + 1] = D1;
    // 类似于差分数组,后减前
	dis[N + 1] = D1 - dis[N];
	for (int i = N; i >= 1; i--)
		dis[i] = dis[i] - dis[i - 1];

	s = C * D2;
	// sum_price储存总油费,sum_volume储存剩余油量
	// 初始化
	double sum_price = C * P;
	double sum_volume = C;
	Gas start = { 0,P };
	// 第N+1个加油站赋值为一个较大的数
	pri[0] = P, pri[N + 1] = 0x3f3f3f;
	// flag记录是否可以到达终点
	bool flag = 1;
	// gas初始化,从第一个加油站开始
	gas.index = 1, gas.price = pri[1];
	for (int i = 0; i <= N + 1; i++) {
		if (s < dis[i]) {
			flag = false;
			break;
		}
		// temp记录行驶一个加油站距离后剩余的油量
		double temp = sum_volume - dis[i] / D2;
		if (temp < 0) {
			// 将出发点重置为gas储存的加油站
			i = gas.index;
			// gas储存的加油站向后延伸,因为比较时不能让与起始点比较
			gas.index++;
			gas.price = pri[gas.index];
			// 重置出发点
			start = { i,pri[i] };
			// 计算加满油所需的钱
			sum_price += (C - sum_volume) * pri[i];
			sum_volume = C;
		}
		else {
			// 比较gas储存的加油站油价和现在加油站的油价
			if (pri[i] < gas.price) {
				gas.index = i;
				gas.price = pri[i];
			}
			// 减去路程中消耗的油量
			sum_volume -= dis[i] / D2;
			if (start.price >= pri[i]) {
				// 把剩余的油卖出
				sum_price -= sum_volume * start.price;
				// 加满油
				sum_price += C * pri[i];
				start.price = pri[i];
				start.index = i;
				gas.index = i + 1;
				gas.price = pri[i + 1];
				sum_volume = C;
			}
		}

	}
	// 减去最后剩余的油
	sum_price -= sum_volume * start.price;

	// 输出
	if (!flag)
		cout << "No Solution";
	else
		cout << fixed<<setprecision(2)<<sum_price;


	return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值