NOIP2015普及组复赛T4——推销员

题目描述

阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。

螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。

螺丝街一共有 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 S1S2Sn<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 1N105

输入样例:

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 家住户推销产品的能积累最大疲劳值只有两种情况:

  1. 推销给 A i A_i Ai值最大的x
  2. 推销给 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家推销产品的最大疲劳值就是下面两种情况的最大值:

  1. 推销给 A i A_i Ai值最大的x家,即s[i] + 2 * f[i]
  2. 推销给 A i A_i Ai值最大的x - 1家,然后最后一家尽可能的远离得远,即s[i - 1] + g[i]

时间复杂度

预处理前缀和数组和求每个 x 对应的最大花费都是线性的,所以算法的瓶颈在于排序算法,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

代码实现

#include 
#include 
#include 
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;
}

你可能感兴趣的:(基础算法,信息学奥赛,贪心算法)