Files
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

4.5 KiB
Raw Permalink Blame History

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

二、解题思路

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

sort(a + 1, a + 1 + n,cmp);
//其中cmp为从大到小排序

Q:什么是X? 答:向 X 家住户推销产品

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

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

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

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

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

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

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

因为已经从大到小排序了,所以,如果舍去$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;
}