题目描述
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。
螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。
螺丝街一共有 N N N家住户,第i家住户到入口的距离为 S i S_i Si米。
由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。
阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。
阿明每走 1 1 1米就会积累 1 1 1点疲劳值,向第 i i i家住户推销产品会积累 A i A_i Ai点疲劳值。
阿明是工作狂,他想知道,对于不同的 X X X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
输入格式
第一行有一个正整数 N N N,表示螺丝街住户的数量。
接下来的一行有 N N N个正整数,其中第 i i i个整数 S i S_i Si表示第i家住户到入口的距离。数据保证 S 1 ≤ S 2 ≤ … ≤ S n < 1 0 8 S1≤S2≤…≤Sn<10^8 S1≤S2≤…≤Sn<108。
接下来的一行有 N N N个正整数,其中第 i i i个整数 A i A_i Ai表示向第i户住户推销产品会积累的疲劳值。数据保证 A i < 1 0 3 A_i<10^3 Ai<103。
输出格式
输出N行,每行一个正整数,第i行整数表示当 X = i X=i X=i时,阿明最多积累的疲劳值。
数据范围
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1≤N≤105
输入样例:
5
1 2 3 4 5
1 2 3 4 5
输出样例:
15
19
22
24
25
算法思想(贪心+前缀和)
输入样例分析:
- 当
x = 1
时,阿明最多积累的疲劳值是到第5家推销商品,ans = 5 + 2 * 5 = 15
- 当
x = 2
时,阿明最多积累的疲劳值是到第4、5家推销商品,ans = 4 + 5 + 2 * 5 = 19
- 当
x = 3
时,阿明最多积累的疲劳值是到第3、4、5家推销商品,ans = 3 + 4 + 5 + 2 * 5 = 22
- 当
x = 4
时,阿明最多积累的疲劳值是到第2、3、4、5家推销商品,ans = 2 + 3 + 4 + 5 + 2 * 5 = 24
- 当
x = 5
时,阿明最多积累的疲劳值是到第1、2、3、4、5家推销商品,ans = 1+ 2 + 3 + 4 + 5 + 2 * 5 = 25
通过分析样例可以发现,阿明向 x
家住户推销产品的能积累最大疲劳值只有两种情况:
- 推销给 A i A_i Ai值最大的
x
家 - 推销给 A i A_i Ai值最大的
x - 1
家,然后最后一家尽可能的远离得远
因此可以利用贪心思想,将所有住户按照 A i A_i Ai从大到小排序。然后,为了方便计算,可以利用前缀和的思想预处理出下列数组:
s[i]
表示向前i
家住户推销产品的疲劳值 A i A_i Ai之和f[i]
表示从入口到前i
家住户距离 S i S_i Si的最大值g[i]
表示从第i~n
家 2 × S i + A i 2\times S_i +A_i 2×Si+Ai的最大值,即第i
家来回的距离 + 到第i
推销商品疲劳值的最大值。
那么,向x
家推销产品的最大疲劳值就是下面两种情况的最大值:
- 推销给 A i A_i Ai值最大的
x
家,即s[i] + 2 * f[i]
- 推销给 A i A_i Ai值最大的
x - 1
家,然后最后一家尽可能的远离得远,即s[i - 1] + g[i]
时间复杂度
预处理前缀和数组和求每个 x
对应的最大花费都是线性的,所以算法的瓶颈在于排序算法,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
代码实现
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 100010;
PII a[N];
int s[N]; //s[i]表示前i户Ai的和
int f[N]; //表示前i户Si的最大值
int g[N]; //表示i~n中2Si + Ai的最大值
int main()
{
int n;
scanf("%d", &n);
//输入Si
for(int i = 1; i <= n; i ++) scanf("%d", &a[i].second);
//输入Ai
for(int i = 1; i <= n; i ++) scanf("%d", &a[i].first);
//按照Ai从大到小排序
sort(a + 1, a + n + 1);
reverse(a + 1, a + n + 1);
//预处理s[i],表示前向前i家住户推销产品的疲劳值前缀和
for(int i = 1; i <= n; i ++) s[i] = s[i - 1] + a[i].first;
//预处理f[i],表示从入口到前i家住户距离Si的最大值
for(int i = 1; i <= n; i ++) f[i] = max(f[i - 1], a[i].second);
//预处理g[i],表示2 * Si + Ai的最大值
//注意,这里从后向前计算, 先计算出小Ai值对应的g[i]
//因为g[i]表示的是从i~n家2*Si +Ai最大值
for(int i = n; i >= 1; i --) g[i] = max(g[i + 1], 2 * a[i].second + a[i].first);
//输出
for(int i = 1; i <= n; i ++)
{
printf("%d\n", max(s[i] + 2 * f[i], s[i - 1] + g[i]));
}
return 0;
}