洛谷P1180 驾车旅游(模拟+搜索)

题目传送门:https://www.luogu.com.cn/problem/P1180

题目描述 

如今许多普通百姓家有了私家车,一些人喜爱自己驾车从一个城市到另一个城市旅游。自己驾车旅游时总会碰到加油和吃饭的问题,在出发之前,驾车人总要想方设法得到从一个城市到另一个城市路线上的加油站的列表,列表中包括了所有加油站的位置及其每升的油价(如 3.25 元/L)。驾车者一般都有以下的习惯:

  1. 除非汽车无法用油箱里的汽油达到下一个加油站或目的地,在油箱里还有不少于最大容量一半的汽油时,驾驶员从不在加油站停下来;
  2. 在第一个停下的加油站总是将油箱加满;
  3. 在加油站加油的同时,买快餐等吃的东西花去 20 元。
  4. 从起始城市出发时油箱总是满的。
  5. 加油站付钱总是精确到 0.1 元(四舍五入)。
  6. 驾车者都知道自己的汽车每升汽油能够行驶的里程数。

现在要你帮忙做的就是编写一个程序,计算出驾车从一个城市到另一个城市的旅游在加油和吃饭方面最少的费用。

输入格式

第一行是一个实数,是从出发地到目的地的距离(单位:km)。

第二行是三个实数和一个整数,其中第一个实数是汽车油箱的最大容量(单位:L);第二个实数是汽车每升油能行驶的公里数;第三个实数是汽车在出发地加满油箱时的费用(单位:元);一个整数是 1 到 50 间的数,表示从出发地到目的地线路上加油站的数目。

接下来 n 行都是两个实数,第一个数表示从出发地到某一个加油站的距离(单位:km);第二个实数表示该加油站汽油的价格(单位:元)。

数据项中的每个数据都是正确的,不需判错。一条线路上的加油站根据其到出发地的距离递增排列并且都不会大于从出发地到目的地的距离。

输出格式

输出一个实数,即精确到 0.1 元的最小的加油和吃饭费用。

输入输出样例

输入 #1

600
40  8.5  128  3
200  3.52
350  3.45
500  365

输出 #1

13133.2

题目分析

按照题目要求可以分析出汽车在当前加油站只有两种情况:1.油箱剩余油量不够到达下一个加油站,必须在此加油站加油且根据题目要求加油要加满;2.油箱剩余油量够到达下一个加油站时再深度细分成两种情况:2.1.如果剩余油量大于汽车油箱总容量的一半时不在此加油站加油;2.2.如果剩余油量小于总容量的一半时可以在此加油站加油也可以不加油。简单分析后进行模拟搜索时可以分成以下两种情况:1.加油且加满;2.不加油。

搜索代码
加油部分(剩余油量不够到达下一个加油站或者在此加油站剩余油量小于总量的一半)加油部分搜索时对剩余油量能够到达下一加油站但却小于总容量的一半时也进行了加油的搜索

if (last * dist2 < d || last < L / 2 )//如果油量不够到下一个加油站或者油量小于总量的一半时在当前加油站加满油 
	{
			dfs(i+1, money+ (L - last) * p_i[i] + 20, L - d / dist2);//花费金额加上在当前加油站的开销,剩余油量为到达下一个加油站时的所剩油量 
	}

不加油部分(剩余油量够到达下一个加油站)不管剩余油量的多少都不加油(剩余油量就算小于总量的一半也不加油搜索)

if (last * dist2 >= d)//不加油,到下一个加油站再说
	{
		dfs(i+1, money, last - d / dist2);//剩余金额不变,剩余油量为到达下一个加油站时的所剩油量
	}

上面的棕字处理实际上将汽车在当前加油站中油箱剩余油量够到达下一个加油站时但剩余油量小于总容量的一半的两种情况——加油或不加油都进行了搜索处理确保得到最优解

将最后抵达的城市设为途中的最后一个加油站方便处理

d_i[n + 1] = dist1;//将你所去的城市(即终点)设为最后一个加油站

整体在每一个加油站进行分析模拟,然后模拟搜索需要加油和不加油的加油站。

完整代码(已成功ac)

#include <iostream>
#include <cstdio>
using namespace std;
double dist1, dist2, pay, L;
//分别为出发地到目的地的距离,汽车每升油能行驶的公里数,在出发地加满油箱时的费用,油箱的最大容量
double d_i[51], p_i[51];//第1个为出发地到某一个加油站的距离,第2个为该加油站汽油的价格
double cost = 999999999;//假设花费最大(实际不可能)
int n;//从出发地到目的地线路上加油站数目
void dfs(int i, double money, double last)//当前所在加油站,当前花费金额,当前剩余油量
{
	if (money >= cost) 
		return;//可行性优化
	if (i > n)//到达城市(所设的第n+1个加油站)
	{
		cost = money;
		return;
	}
	double d = d_i[i + 1] - d_i[i];//当前加油站距离下一个加油站的距离 
	if (last * dist2 < d || last < L / 2 )//加油
	{
			dfs(i+1, money+ (L - last) * p_i[i] + 20, L - d / dist2); 
	}
	if (last * dist2 >= d)//不加油
	{
		dfs(i+1, money, last - d / dist2);
	}
}
int main()
{
	cin >> dist1;
	cin >> L >> dist2 >> pay >> n;
	for (int i = 1; i <= n; i++)//输入各加油站距初始城市的距离和油价
	{
		cin >> d_i[i] >> p_i[i];
	}
	d_i[n + 1] = dist1;//将你所去的城市(即终点)设为最后一个加油站
	dfs(1, pay, L - d_i[1] / dist2);//假设已经到了第1个加油站
	printf("%.1lf", cost);//保留一位小数输出
	return 0;
}

题目分析是从每一个加油站开始的,利用回溯和优化减少搜索时间,本题变量较多,最好用英文缩写区分,数组和假设花费值也可以调高,题目分析后发现这道题并不难,下次发一道和这题类似但稍复杂一点的题目。

计算机204 zjr

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值