题目大意:一条路上有n户人,推销疲劳值是Ai,到出口的距离是Si,推销员每走1米就积累1点疲劳值,不走多余的路,推销k户人的最大疲劳值是多少?
题目描述
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有 N 家住户,第 i 家住户到入口的距离为 Si 米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。阿明会从入口进入,依次向螺丝街的 X 家住户推销产品,然后再原路走出去。 阿明每走 1 米就会积累 1 点疲劳值,向第 i 家住户推销产品会积累 Ai 点疲劳值。阿明是工作狂,他想知道,对于不同的 X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
输入
第一行有一个正整数 N,表示螺丝街住户的数量。
接下来的一行有 N 个正整数,其中第 i 个整数 Si 表示第 i 家住户到入口的距离。
数据保证 S1≤S2≤…≤Sn<108。 接下来的一行有 N 个正整数,其中第 i 个整数 Ai 表示向第 i 户住户推销产品会积累的疲劳值。数据保证 Ai<103。
输出
输出 N 行,每行一个正整数,第 i 行整数表示当 X=i 时,阿明最多积累的疲劳值。
样例输入
5
1 2 3 4 5
1 2 3 4 5
样例输入2
5
1 2 2 4 5
5 4 3 4 1
样例输出
15
19
22
24
25
样例输出2
12
17
21
24
27
提示
对于 20%的数据,1≤N≤20;
对于 40%的数据,1≤N≤100;
对于 60%的数据,1≤N≤1000;
对于 100%的数据,1≤N≤100000。
【输入输出样例 1 说明】
X=1: 向住户 5 推销,往返走路的疲劳值为 5+5,推销的疲劳值为 5,总疲劳值为 15。
X=2: 向住户 4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值为 4+5,总疲劳 值为 5+5+4+5=19。
X=3: 向住户 3、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值 3+4+5,总疲 劳值为 5+5+3+4+5=22。
X=4: 向住户 2、3、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值 2+3+4+5, 总疲劳值 5+5+2+3+4+5=24。
X=5: 向住户 1、2、3、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值 1+2+3+4+5, 总疲劳值 5+5+1+2+3+4+5=25。
【输入输出样例 2 说明】
X=1:向住户 4 推销,往返走路的疲劳值为 4+4,推销的疲劳值为 4,总疲劳值 4+4+4=12。
X=2:向住户 1、4 推销,往返走路的疲劳值为 4+4,推销的疲劳值为 5+4,总疲劳值4+4+5+4=17。
X=3:向住户 1、2、4 推销,往返走路的疲劳值为 4+4,推销的疲劳值为 5+4+4,总疲劳值 4+4+5+4+4=21。
X=4:向住户 1、2、3、4 推销,往返走路的疲劳值为 4+4,推销的疲劳值为 5+4+3+4, 总疲劳值 4+4+5+4+3+4=24。或者向住户 1、2、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值为 5+4+4+1,总疲劳值 5+5+5+4+4+1=24。
X=5:向住户 1、2、3、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值为 5+4+3+4+1, 总疲劳值 5+5+5+4+3+4+1=27。
NOIP2015普及组第四题
解题思路
暴力贪心做法:推销1户,遍历查找Si*2+Ai的最大值即可;推销2户,在1户的基础上,查找下一户j的最大疲劳值,如果编号j比i小,那么疲劳值是Aj,否则疲劳值是Sj*2+Aj。这样,没多一户从剩下没到过的查找最大值,找到后标记为0下次就不会重复推销了。为什么是正确的呢?为什么找下一户的时候,之前的还是要去推销?如果下一户j更远,也不可能只选择更远的而不去前一户i,因为Ai >= Aj + (Sj-Si)*2;如果下一户j更近,也不可能不去前一户i,因为Aj <= Ai + (Si-Sj)*2,之所以加2倍路程差,因为我们可以假定i是最远的,如果不是,要不去i,更远的先不去了(要么就是Ai >= Aj)。
优先队列/堆优化:既然我们每次要找最远的左边的最大疲劳值,或者最远的右边的最大值(疲劳值+2倍路程差),为什么不用优先队列优化呢?开两个优先队列,先全部入队:一个队列qx记录疲劳值和编号,一个队列qy记录最大值和编号;推销1户的特殊处理,处理玩后标记该户到过。下一次,就从两个优先队列的最值里面找最大的。qx中最大的和qy中最大的进行比较,需要注意的是,qy中最大的如果在左边(编号比最远的小),不需要也不能假设2倍路程差,要继续找下一个最大值;qx则不需要这样处理,因为即使找到右边的(编号比最远的大),qy中必能找到一个不比他小的,而且qx也不能直接删掉右边的,因为后面可能把右边的归类到左边(出现一个更远的)。
贪心倒推:每次找最大疲劳值,有i户推导i+1户,能不能倒着来呢?可以的。最值结果是n户都推销,疲劳值是Sn*2+A1+A2+…+An,那推销n-1户的最大值,就是从n户中减去疲劳值最小的那一户。我们可以按疲劳值进行排序,那么减去的第一户,必定是最小疲劳值的两个之一:如果最小的那个不是最远的,第n次去的是b[1].i;如果最小的那个是最远的,那么他需要加上2倍路程差,此时次小的b[2].i就有可能是最优的,需要比较过才知道。如果第n次去的是i,下次继续从b[2]到b[n]去找就行;如果是i+1,那么b[1]还没去过,为了保证下次还是从b[2]到b[n]查找,令b[2]=b[1]即可。依次“不去”最小疲劳值的那些户,就可以倒推出推销k(n..1)的最大疲劳值了。在此过程中,我们需要计算路程差,故需要知道最远去到哪(y)、次远去到哪(x)。代码中数组a记录原始数据,i记录编号,标号为0表示没去过,倒推初始时全都去过;数组b按疲劳值排序,这样就可以依次选择最小疲劳值了。(最远次远相等,路程差为0;只有最小疲劳值需要加2倍路程差,后面的加了没有影响,因为还是选择最小的,后面的本来就不是最小了,加了还是不是最小。)
程序实现
贪心倒推满分:
优先队列优化的贪心,满分:
暴力贪心60分: