P9749 [CSP-J 2023] 公路

题目描述

小苞准备开着车沿着公路自驾。

公路上一共有 n 个站点,编号为从 1 到 n。其中站点 i 与站点 i+1 的距离为 vi 公里。

公路上每个站点都可以加油,编号为 i 的站点一升油的价格为 ai 元,且每个站点只出售整数升的油。

小苞想从站点 1 开车到站点 n,一开始小苞在站点 1 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 d 公里。问小苞从站点 1 开到站点 n,至少要花多少钱加油?

输入格式

输入的第一行包含两个正整数 n 和 d,分别表示公路上站点的数量和车每升油可以前进的距离。

输入的第二行包含 n−1 个正整数 v1,v2…vn−1​,分别表示站点间的距离。

输入的第三行包含 n 个正整数 a1,a2…an,分别表示在不同站点加油的价格。

输出格式

输出一行,仅包含一个正整数,表示从站点 1 开到站点 n,小苞至少要花多少钱加油。

输入输出样例

输入 #1复制

5 4
10 10 10 10
9 8 9 6 5

输出 #1复制

79

说明/提示

【样例 1 解释】

最优方案下:小苞在站点 1 买了 3 升油,在站点 2 购买了 5 升油,在站点 4 购买了 2 升油。

【样例 2】

见选手目录下的 road/road2.in 与 road/road2.ans。

【数据范围】

对于所有测试数据保证:1≤n≤10^5,1≤d≤10^5,1≤vi≤10^5,1≤ai​≤10^5。

测试点n≤n≤特殊性质
1∼58
6∼1010^3
11∼1310^5A
14∼1610^5B
17∼2010^5
  • 特殊性质 A:站点 1 的油价最低。
  • 特殊性质 B:对于所有 1≤i<n,vi 为 d 的倍数。

解题思路:

这道题可以考虑贪心,让车跑尽可能多的路程之后再去加油,由于车的油箱是无限大的,所以可以先考虑能跑到哪一个站点,然后在这些可以跑到的站点中选择一个价钱最低的加油,然后向下一个站点开,一直重复直到最后到达最后一个站点。

这个不能叫做贪心了,是贪心+反悔(回去加油)

先放上代码:

AC code: 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define FOR(x,a,b,c) for(int x=a;x<=b;x+=c)
#define MFOR(x,a,b,c) for(int x=a;x>=b;x-=c)
#define MPFOR(x,a,b,c) for(int x=a;a<=b;x*=c)
const int N3=1e3+10;
const int N=1e6+10;
const long double esp=1e-8;
int gcd(int a,int b){
	int c=a%b;
	while(a%b!=0){
		a=b;
		b=c;
		c=a%b;
	}
	return b;
}
int lcm(int x,int y){
	return (x*y)/gcd(x,y);
}
ll a[N],b[N],c[N];//a:存放距离 b:存放距离起点的距离 c:存放价钱
priority_queue<ll> q;//可以用一个ll minx=100010代替
//做的时候脑子抽风写了个优先队列提升时间复杂度,真的没必要,直接算到当前最小的加油站价格的值就行,小顶堆的话如果一个加油站不是无限油量才有用
ll ans=0;//最后的付的钱
ll n,d;
ll sum=0;//当前这个站点离着起始点有多远(不必须写,不写也就麻烦一点)
ll nowi=0;//记录当前可以开到距离起点多远的地方
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%lld %lld",&n,&d);
	a[1]=0;
	FOR(i,2,n,1){
		scanf("%lld",&a[i]);
		sum+=a[i];
		b[i]=sum;
	}
	FOR(i,1,n,1){
		scanf("%lld",&c[i]);
	}
//	FOR(i,1,n,1){
//		cout<<b[i]<<" ";
//	}
	for(int i=1;i<=n;i++){
		if(nowi>=b[i]){
			q.push(-c[i]);//入堆,可以用minx=min(minx,c[i])代替
		}
		else{
			ll t=b[i]-nowi;
			ll tt=ceil(1.0*t/d);
			nowi+=tt*d;
			ans+=tt*(-q.top());//加上这次加油需要的钱,可以用ans+=tt*minx代替
			q.push(-c[i]);//现在可以走到这里,放入可以加油的站点中,可以用minx=min(minx,c[i])代替
		}
	}
	printf("%lld\n",ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

需要注意的几个点:

1.开long long,因为一个距离和油量到了10^5,距离最后会到10^10,int类型直接炸,int会WA一半,50分。

2.我这个小顶堆真的没用,实际上这样反倒会增加时间复杂度变为O(nlogn),还好不是卡着10^7的数据范围,不然用小顶堆会时间会炸。

那要是不会写贪心咋办?

15分很好拿

特殊性质 A:站点 1 的油价最低。

所以可以直接从站点1加上可以到终点的油量,然后中途不再加油,15分拿到。

剩下特殊性质B没怎么想,因为我会贪心。

有任何错误请评论指正作者将给予修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值