题解:P2672 [NOIP 2015 普及组] 推销员

题目传送门

是道很好的题
代码实现难度很低很低
但是基础的思维量还是能保证的
但是建议调绿
十五分钟就写完了
关键词:贪心前后缀


简化题意

给定两个长度为N的序列:
\(S_i\):表示第i家住户到入口的距离,保证\(S_1 \leq S_2 \leq \cdots \leq S_N < 10^8\)
\(A_i\):表示向第i家住户推销产品获得的疲劳值,保证\(A_i < 1000\)
对于每个\(k \in [1,N]\),我们需要选择一个包含k家住户的集合\(K\),使得总疲劳值最大。

总疲劳值的计算公式为:

\[f(k) = \sum_{i \in K} A_i + 2 \times \max_{i \in K} S_i \]

其中:
\(\sum_{i \in K} A_i\)表示推销k家住户的总疲劳值
\(2 \times \max_{i \in K} S_i\)表示往返最远住户的路程疲劳值


简化思考与解题思路

核心观察

$\ \ $ 疲劳值构成分析
$\ \ $ $\ \ $ 总疲劳值由两部分组成:推销疲劳值之和与路程疲劳值
$\ \ $ $\ \ $ 路程疲劳值仅取决于最远访问的住户,与访问的其他住户距离无关

贪心策略:

对于给定的k,最优解有两种可能:
$\ \ $ a) 选择前k个最大的\(A_i\),加上这些住户中最远的\(S_i \times 2\)
$\ \ $ b) 选择前k-1个最大的\(A_i\),再加上一个距离更远的住户(即使其\(A_i\)较小)
数学表达: 对于每个k,最优解为:

\[f(k) = \max\left(\displaystyle{pre}[k] + 2 \times \displaystyle{max\_dis}[k]\ ,\ \displaystyle{pre}[k-1] + \displaystyle{suf}[k+1]\right) \]

其中:
\(pre[k]\):前k个最大\(A_i\)的和
\(max\_dis[k]\):前k个选择中的最大\(S_i\)
\(suf[k+1]\):从k+1开始最大的\((2S_i + A_i)\)

算法实现要点

预处理步骤:

将住户按\(A_i\)降序排序,计算前缀和\(\textstyle {pre}\)
按原始\(S_i\)顺序计算后缀最大值\(\textstyle{suf}\),存储\((2S_i + A_i)\)

动态维护:

在遍历排序后的数组时,维护当前选择中的最大距离\(\textstyle{max\_dis}\)
对于每个k,比较两种策略取最大值

复杂度分析:

排序:\(O(N \log N)\)
预处理:\(O(N)\)
查询:\(O(N)\)
总复杂度:\(O(N \log N)\),完全满足题目数据范围要求


我们上CODE

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int n, ans[N], now, suf[N], nid, pre[N];

struct node {
    int c, x, id;
} a[N];

bool cmp(node a, node b)
{
    return a.c > b.c;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i].x;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].c;
        a[i].id = i;
    }
    for (int i = n; i >= 1; i--)
        suf[i] = max(suf[i + 1], 2 * a[i].x + a[i].c); // 默认按照距离升序,处理贡献后缀
    sort(a + 1, a + 1 + n, cmp); // 换成权值降序
    for (int i = 1; i <= n; i++)
        pre[i] = pre[i - 1] + a[i].c; // 处理前缀权值
    for (int i = 1; i <= n; i++) {
        if (a[i].x > now) {
            now = a[i].x;
            nid = a[i].id;
        }
        cout << max(pre[i] + 2 * now, pre[i - 1] + suf[nid + 1]) << endl;
    }
    return 0;
}

$\ $
文章由LLM润色
但是本人作出绝大部分贡献
LLM仅应用于排版和语文方面

posted @ 2025-10-19 19:24  Noivelist  阅读(13)  评论(0)    收藏  举报