You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 464. 推销员

一、理解测试用例

s[i]: 1 2 3 4 5
a[i]: 1 2 3 4 5

① 取1个:5*2+5=15 ② 取2个:(5+4)+2*5=19 ③ 取3个:(5+4+5)+2*5=22 ④ 取4个:(2+3+4+5)+2*5=24 ⑤ 取5个:(1+2+3+4+5)+2*5=25

二、解题思路

距离是真的烦人,那就先不管它吧。因为想要疲劳值最大,那么先 按照疲劳值从大到小排序 一遍。

此时,可以判断出,最大值 可能 为:对于每个X,只要加上前X大的疲劳值,再加上 这些数中距离最远 的并 乘以2,也就是:\sum_{i=1}^{X}(a[i])+s[j]2$$

其中,s[j]表示前 X 个距离最远的。

但是,推销员可以通过 走远一点,虽然疲劳值比前面小,但是有可能把路程一算,反而更大。

因此,可以把a[],即疲劳值中,X大中的最小值,即第X大的那一家舍去,看看能不能通过走更远来换取更大的疲劳值总和。

而从后面看,一定也会存在一个最大值,把这个最大值和前面X1个疲劳值总和加起来,看看会不会比前面第一种的情况大。

证明:只需舍去最小值来走更远,无需舍去更多数来走更远。

其实这也不是一个证明..

因为已经从大到小排序了,所以,如果舍去2个疲劳值,那么后面只会在加上两个更小的疲劳值,以及两个之间最大的距离并乘2,那么这样还不如只舍去一个。

2个不行,那么2个以上更是不行了。

举栗子

硬讲太累了,还是用一个例子吧。

s[i]:1,3,4,5,11
a[i]:5,4,2,1,1
  • X==1:
    • 直接取,值为:1×2+5=7
    • 如果舍去当前(最小)的往后跑,值为:11×2+1=23

所以,最大值就为max(7,23)=23

  • X==2:
    • 直接取,值为:3×2+5+4=15

    • 把其中 最小 值舍去,也就是4去掉,往后跑值为:11×2+1+5=28,那么最大值为max(15,28)=28

    • 若把次小疲劳值,即5,也往后跑,那么值为11×2+1=23,可以发现,距离并未因此改变,仍为11,而位置移后,疲劳值只会减少,故只能移动最小疲劳值。

那么,最后一个问题,酱紫复杂度是会炸掉的,原因分析: 我们需要哪些信息呢? sum[i]:a[i]数组前i位的前缀和,比如上面的5+4

② 除了计算疲劳值的前缀和,还需要一个前i个住房中,找到最大的距离值,这是题目中要求的数据,它能不能预处理出来呢?答案是可以的:从i \sim n一路max pk不就记录下来了吗~ 我们用g[i]来记录这个信息。

③ 把前i位中第i位去掉,保留前i-1位,那么,需要知道后面这一堆住房中距离的最大值。 很明显,这里在倒序由后向前进行PK记录即可。这里有一个坑点,就是如果你决策了选择某个住房,那么这个距离的最大值你肯定是妥妥的享用了,但不要忘记还要加这住房的疲劳值a[i].b

这样子预处理,就可以实现O(nlogn)qaq

最终二选一就可以了~

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;                  // n个客户
int sum[N], g[N], h[N]; // 疲劳值前缀和 前i个最大值 后i个最大值
struct Node {
    int s; // 距离
    int b; // 疲劳
    const bool operator<(const Node &W) {
        return b > W.b;
    }
} a[N];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i].s; // 住户i到入口的距离
    for (int i = 1; i <= n; i++) cin >> a[i].b; // 向i住户推销产品会积累的疲劳值

    sort(a + 1, a + 1 + n); // 按疲劳由大到小排序

    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i].b;              // 预算出疲劳值的前缀和
    for (int i = 1; i <= n; i++) g[i] = max(g[i - 1], 2 * a[i].s);          // 前i个距离最大值
    for (int i = n; i >= 1; i--) h[i] = max(h[i + 1], 2 * a[i].s + a[i].b); // 后i个距离最大值
    for (int i = 1; i <= n; i++) printf("%d\n", max(sum[i] + g[i], sum[i - 1] + h[i]));
    return 0;
}