跳转至

D - 新居规划

基本信息

题目出处2023 广东省大学生程序设计竞赛
队伍通过率189/295 (64.1%)

题解

如果已知 \(k\)\(2 \le k \le n\))个人有邻居,剩下的人没有邻居,怎样选择有邻居的人才能使总满意度最大化?

这是一个经典问题。先假设所有人都是没邻居的,得到总满意度 \(\sum\limits_{i = 1}^n b_i\)。当第 \(i\) 个人从没邻居变成有邻居时,总满意度将增加 \((a_i - b_i)\)。因此选择 \((a_i - b_i)\) 最大的 \(k\) 个人变成有邻居的即可。排序后可以在 \(\mathcal{O}(n)\) 的复杂度内一次性算出 \(k = 2, \cdots, n\) 的最大总满意度。

如果 \(k\) 个人有邻居,剩下的人没有邻居,这样的布局至少需要 \(k + 2(n - k) = 2n - k\) 栋房子(即有邻居的人都住在最左边,然后每隔一栋房子住一个没邻居的人)。因此只有满足 \(2n - k \le m\) 才能考虑。

最后,别忘了考虑所有人都没有邻居的情况。这要求 \(m \ge 2n - 1\)

复杂度 \(\mathcal{O}(n\log n)\)。主要是排序的复杂度。

参考代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
#define MAXN ((int) 5e5)
using namespace std;

int n, m, A[MAXN + 10], B[MAXN + 10];

void solve() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d%d", &A[i], &B[i]);

    // 将 (A[i] - B[i]) 排序,vector 里存的是从小到大的顺序
    vector<int> vec;
    for (int i = 1; i <= n; i++) vec.push_back(A[i] - B[i]);
    sort(vec.begin(), vec.end());

    long long ans = 0, now = 0;
    for (int i = 1; i <= n; i++) now += B[i];
    // 特殊情况:所有人都没有邻居
    if (m >= 2 * n - 1) ans = now;

    now += vec[n - 1];
    for (int i = 2; i <= n; i++) {
        // 计算有 i 个人有邻居时的最大总满意度
        now += vec[n - i];
        if (2 * n - i <= m) ans = max(ans, now);
    }
    printf("%lld\n", ans);
}

int main() {
    int tcase; scanf("%d", &tcase);
    while (tcase--) solve();
    return 0;
}