贪心 模拟:CSP-J 2023 公路

原文链接:CSP-J真题第二讲:公路

本题有视频讲解,链接:CSP-J 2023 T2 公路_哔哩哔哩_bilibili

说明:优快云和公众号文章同步发布,需要第一时间收到最新内容,请关注公众号【比特正传】。

上篇文章发布后,收到公众号后台私信,让我多写一些第2、3、4题的题解,读者如此合理的要求,必须满足。今天来看一篇2023年的普及组的第二题:公路。

一、题目背景

题目来源:CSP-J 2023年 T2

题目考察点:贪心,模拟

题目链接:

[CSP-J 2023] 公路 - 洛谷

题意:从站点1到站点n,车的油箱无限大(即可以装任意多油),每个站点油价各不相同,问从站点1到站点n的最小花费。

二、题目分析

做竞赛题呀,这个数据范围非常重要,关注一下数据规模【n\leqslant 10^{5}】,可以判断出这道题的复杂度不能超过O(n^{2}),O(n)或O(nlogn)复杂度比较合适,因此思考方向有了。

有两种方法解决,两种方法背后的本质是一样的,实现不一样,我们先看第一种,理解了第一种,更容易理解第二种。

方法一:

如果是你,你会选择在哪里加油?肯定是找便宜的站点加油。

在第一站的时候,只能在第一站加油,那么第一站加多少呢?可以很容易想到,加的油刚好满足我到达下一个比我更便宜的站点,因此找下一个比当前站点油价更便宜的站点,假设为站点x,然后当前站点加的油刚好满足我到站点x即可。

还有一点,因为每次加油必须加整数升油,因此到第x站点后,可能还会剩一点油,因此下一趟优先使用上一趟剩的油,不够了再加油。

AC code

#include "iostream"
#include "cmath"
using namespace std;
const int N = 1e5+7;
/**
 * 在某个站点处,找包含自己的前面所有站点油价最低的加即可
 **/
long long n, d, curDist, v;
long long ans = 0;  // 记录花费总和 
long long exist = 0;  // 记录到达当前站点时,之前加的油用剩下的还可以跑的距离 
int minCost; // 记录当前站点及之前的所有站点中,最低油价 
int dist[N], cost[N];
int main() {
	scanf("%d%d", &n, &d);
	for(int i=1; i<n; i++) scanf("%d", &dist[i]);
	for(int i=1; i<=n; i++) scanf("%d", &cost[i]);
	int i = 1;
	while(i < n) {
		int j = i;  // 双指针
		curDist = 0;
		while(j < n && cost[j] >= cost[i]) curDist += dist[j++];  // 找到下一个比第i站点更便宜的加油站 
		if(curDist <= exist) {  // 如果下一趟跑的距离,用上一趟剩余的油可以满足,那么本次就不用加油 
			exist -= curDist;
			i = j;
			continue;
		}
		curDist -= exist; // 先把之前剩余的油用了 
		v = curDist / d; // 计算本次要加的油量 
		if(curDist % d > 0) v++;  // 如果有零头,还需要多加一升 
		ans += v * cost[i];  // 计算费用 
		exist = v * d - curDist;  // 计算剩余的油量所能跑的距离数 
		i = j;   // 更新i 
	} 
	printf("%lld\n", ans);
	return 0;
}

方法二:

如果是你,你会选择在哪里加油?肯定是找便宜的站点加油,如果现在位于第x站点,如果需要加油,那么我们一定会选择包含x站点以前的所有站点中,油价最低的站点(因为油箱无限大,所以在当前站点,可以加前面某一站点的油,等价于在前面站点加油)

有两种方式找x站点之前的油价最低的站点:

方式一:暴力遍历1~x;

方式二:在遍历站点1~n的时候,用一个变量minCost记录当前的最低油价;即minCost=min(minCost, cost[i]);

很明显方式一的复杂度为O(n), 方式二的复杂度为O(1); 因此我们采用方式二寻找最低油价。

还有一点,因为每次加油必须加整数升油,因此可能从第x站点到x+1站点,还会剩一点油,需要记录下来,在下一段路程中,优先用上一段剩余的油跑。

我选择的解决思路是
在每个站点,只加恰好能够跑到下一站点的油,不会多加,但是一直记录当前站点及之前最便宜的站点的油价,比如目前已经到了第8站点,但是第8站点之前,第2站点的油价最便宜,保存在minCost中,那么在第8站点的时候,依旧会用第二站点的油价买油,这等价于在第2站点加了非常多的油,可以支撑车子跑到第8站点。

AC Code

#include "iostream"
#include "cmath"
using namespace std;
const int N = 1e5+7;
/**
 * 在某个站点处,找包含自己的前面所有站点油价最低的加即可
 **/
int n, d;
long long ans = 0;  // 记录花费总和 
long long exist = 0;  // 记录到达当前站点时,之前加的油用剩下的还可以跑的距离 
int minCost; // 记录当前站点及之前的所有站点中,最低油价 
int dist[N], cost[N];
int main() {
	scanf("%d%d", &n, &d);
	for(int i=1; i<n; i++) scanf("%d", &dist[i]);
	for(int i=1; i<=n; i++) scanf("%d", &cost[i]);
	int minCost = cost[1];  // 初始认为一号站点油价最低
	for(int i=1; i<n; i++) {     
		if(exist >= dist[i]) {
			exist -= dist[i];
			continue;
		}
		dist[i] -= exist;  // 用上一段剩下的油先抵消一部分下一段要跑的距离 
		minCost = min(minCost, cost[i]);  // 更新最低油价 
		int v = dist[i] / d;   // 计算加油量 
		if(dist[i] % d > 0) v++;  // 判断是否需要多加一升 
		exist = v * d - dist[i];  // 更新多余的量 
		ans += v * minCost;  // 更新总费用 
	}
	printf("%lld\n", ans);
	return 0;
}

### CSP-J 2023 公路试题解析 #### 题目概述 CSP-J 2023 的复赛中,“公路”是一道经典的优化类问题,主要涉及油料消耗和价格优化。该题目要求选手在有限资源下寻找最优路径规划方案,考验了参赛者的算法设计能力和逻辑思维水平[^1]。 #### 贪心算法的应用 为了高效解决问题,可以采用贪心算法作为核心思路。具体而言,在每一步决策时都选择当前局部最优解,最终达到全局最优的效果。例如,在加油站点的选择上,优先考虑油价较低的位置补充燃料,以此降低整体成本并满足行驶需求。 #### 特殊性质利用 当面对难以直接求解的情形时,可尝试借助题目给出的数据范围或其他隐含条件进行简化操作。比如枚举 [-2000, 2000] 区间内的所有可能值来验证是否存在符合条件的结果,并从中选取最大值作为答案之一;这种方法虽然效率不高但能获得部分分数支持[^3]。 #### 二分法实现 另一种有效的方法是通过二分查找技术缩小目标区间直至逼近真实解。在此基础上调整参数设置使得计算过程更加精确可控。特别注意的是由于实际场景下的时间变量通常为整数或者固定间隔变化因此应该对相应数值做适当转换后再执行迭代运算直到满足精度要求为止最后再还原回原始单位表示形式即可得出最终结论[^4]。 以下是基于上述原理编写的 Python 实现代码: ```python def solve_road_problem(stations, fuel_capacity): n = len(stations) dp = [float('inf')] * (n + 1) dp[0] = 0 for i in range(1, n + 1): distance = stations[i - 1][0] price = stations[i - 1][1] # 寻找最近的加油站 j ,其中 j 到 i 的距离不超过 fuel_capacity max_j = -1 min_price = float('inf') for j in range(i - 1, -1, -1): if distance - stations[j][0] <= fuel_capacity and stations[j][1] < min_price: min_price = stations[j][1] max_j = j if max_j != -1: dp[i] = min(dp[i], dp[max_j] + (distance - stations[max_j][0]) * min_price) return dp[n] # 测试数据 stations = [(0, 5), (100, 4), (200, 3)] fuel_capacity = 150 result = solve_road_problem(stations, fuel_capacity) print(result) ``` 此段代码定义了一个函数 `solve_road_problem` 接受两个参数分别是沿途各处加满一次汽油所能跑的最大公里数列表以及车辆单次充满油箱后最多可行进的距离限制然后返回完成整个旅程所需的最低总费用金额。 #### 输出格式说明 根据题目描述如果存在合法解答则需打印较大的那个实根反之输出字符串 "NO"[^5]. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值