noip 2015 pj 普及组 第四题 推销员 salesman

本文详细介绍了NOIP 2015普及组第四题推销员问题的解题思路。通过分析样例,发现规律,利用动态规划和优先队列(大根堆)优化算法,最终达到O(nlog(n))的时间复杂度,成功解决此题。并提供了程序实现和AC验证。

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

这道题的题目大意就是说一个人要到一段死胡同去推销,拜访第i个门户会消耗a[i]体力,走路也会消耗一些体力,第i个门户距离胡同口的距离是s[i],然后让你输出在不多余走路的情况下拜访1户(x==1),拜访2户(x==2)......拜访n(x==n)户分别最多要消耗多少体力。

初看这道题自己感觉没有任何头绪,于是反复读样例,发现这道题有一定的规律,当x==1时,答案就是最大的s[i]*2+a[i],这个值可以在输完s[i]的时候就判断出来。其中s[i]*2的原因是在死胡同里面走的时候,如果你要走到s[i]的深度且不走多余的路,那么从进去到出来你一定会走2*s[i]的路程,对吧?

那么此时我们已经有了x==1的答案,那么可以猜测我们是不是可以通过x==1来推出x==2,x==3乃至x==n的答案来呢?

首先,可以暴力枚举......不过一看数据规模,n<=100000,简直了,还是算了吧......

然后,继续反复读样例说明,每次x的值增加1的时候,一定会多走一个门户(其实这是一句废话),那么根据这条性质,我们需要多走的那个门户一定就是当前的未走的门户中耗费体力值最大的。ans[i]=ans[i-1]+max(门户消耗的体力),一定是这样的吧?那么,我们是不是可以每当x循环增加1以后,都找出当前还没走的门户消耗体力的最大值,也就是找出max(门户消耗的体力)......嗯,一定是这样......

可是每次这样找最大值就成了O(n^2)的复杂度,明显还是爆......那么该怎么办呢?显然,每次找最大值会进行大量的重复计算,所以我们可以使用优先队列,也就是建立一个大根堆,这样每次只用调用堆顶元素就可以找到当前未走的门户中耗费体力值最大的了。

既然使用优先队列,可以直接用STL里边的模板。

这道题貌似就解决了?NoNoNo!并没有这么简单!你看,我们的情况实际上有两种。一种情况是当前门户比之前最深的门户更深,此时我们需要考虑一下多走的路程所消耗的体力,还有一种情况是当前门户比之前最深的浅一些,这种情况比较简单,关键是第一种情况该如何处理。那么请放开你的脑洞,我们是不是可以建立两个堆,分别储存两种情况的最大值呢?

这就比较复杂了,先建立两个结构体堆,分别命名为lheap和rheap,分别存储较浅的门户和更深的门户,每一个节点分别储存的是

struct rec
{
    int data;//拜访当前户用的体力值 
    int l;    //当前户与路口的距离 
    int sum;//拜访当前户的总体力值 (lheap存的是当前点的访问花费 rheap存的是单独访问当前点的花费) 
    int xb;//数组下标 
};

lheap的sum中由于不用考虑路程(总路程不会改变),所以光存当前门户的拜访花费即可;而rheap则要考虑路程的变化,为了方便计算,直接存储单独访问当前门户的花费,也就是s[i]*2+a[i]的值

既然考虑要判断当前的总路程,我们可以用一个l变量来记录

然后我们可以建立一个ans数组,存储x==1~n的答案,每次比较lheap和rheap的堆顶元素,将总消耗量大的加入到ans[i]中,就可以了

不过还有一个细节要处理,如果当前较大的堆顶

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值