题解:学而思编程 展览会

【题目来源】

学而思编程:展览会

【题目描述】

美术馆有 \(n\) 幅画,在画廊里从西向东排成一列,这些画从西向东编号为 \(1∼n\)。第 \(i\) 幅画在距离最西端 \(x_i\) ​米位置,它的价值为 \(v_i\)​。

明天要召开展览会,来客会非常多,馆长决定只展出其中 \(m\) 幅画,其他画都拿走放到库房里。如果留下的画距离太近,观众们观赏起来会不方便。所以留下的画之间的距离必须大于等于 \(D\)

展览会的“华丽度”定义为展出的 \(m\) 幅画中,价值最低的画的价值。通过适当选择留下的 \(m\) 幅画,能得到最大的“华丽度”是多少。

【输入】

\(1\) 行,\(3\) 个正整数 \(n,m,D\)

接下来 \(n\) 行,每行两个整数 \(x_i,v_i\)

【输出】

输出可以得到的最大“华丽度”。如果无法选出满足要求的画,输出 \(−1\)

【输入样例】

3 2 20
10 250
30 200
50 500

【输出样例】

250

【核心思想】

  1. 问题分析:给定 \(n\) 幅画,每幅画有位置 \(x_i\) 和价值 \(v_i\),需要选出 \(m\) 幅画满足相邻画之间的距离 \(\geq D\),使得选出画中价值最低的那幅画的"华丽度"最大化。这是一个最大化最小值问题,具有单调性:如果华丽度 \(mid\) 可行,则所有小于 \(mid\) 的值也可行。

  2. 算法选择

    • 整数二分(二分答案):二分查找最大可行的"华丽度"
    • 贪心验证:对于给定的华丽度 \(mid\),使用贪心策略判断是否能选出至少 \(m\) 幅价值 \(\geq mid\) 且间距满足要求的画
    • 排序预处理:按位置排序以便贪心选择
  3. 关键步骤

    • 输入与初始化
      • 读入 \(n\)(画的总数)、\(m\)(需要展出的数量)、\(D\)(最小距离要求)
      • 读入每幅画的位置 \(x_i\) 和价值 \(v_i\)
      • 按位置 \(x\) 升序排序所有画
    • 确定二分边界
      • 左边界 \(l = 0\),右边界 \(r = \max(v_i)\)(最大价值)
    • 二分查找最大华丽度
      • 计算 \(mid = (l + r) / 2\)
      • 调用 \(check(mid)\) 验证可行性
      • 若可行:\(ans = mid\)\(l = mid + 1\)(尝试更大值)
      • 若不可行:\(r = mid - 1\)(尝试更小值)
    • 贪心验证函数 \(check(mid)\)
      • 从左到右遍历排序后的画
      • 若当前画价值 \(v_i \geq mid\) 且与上一幅选中画的距离 \(\geq D\),则选择该画
      • 返回是否选择了至少 \(m\) 幅画
    • 输出答案:若 \(ans = 0\) 输出 \(-1\),否则输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log V + n \log n)\),排序 \(O(n \log n)\),二分 \(O(\log V)\) 次,每次验证 \(O(n)\),其中 \(V\) 为最大价值
    • 空间复杂度:\(O(n)\),存储画的信息
  5. 二分答案与贪心验证的核心思想

    • 二分答案技巧:将"求最大/最小满足条件的值"转化为"判断某个值是否满足条件",利用单调性进行二分
    • 贪心选择策略:从左到右能选就选,保证在间距约束下选出最多的画,这是最优的
    • 验证的单调性:若华丽度 \(mid\) 可行,则所有小于 \(mid\) 的值也可行(价值门槛降低,可选的画更多)
    • 排序的作用:按位置排序后,贪心选择才能正确执行,确保相邻画之间的距离计算正确
    • 适用于最大化最小值、最小化最大值、可行性判定类问题

【算法标签】

整数二分

【代码详解】

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;

// 画的结构体
struct paint
{
    int x;       // 画的位置(距离最西端的距离)
    int v;       // 画的价值
} p[N];

// 按位置 x 升序排序
bool cmp(paint a, paint b)
{
    return a.x < b.x;
}

int n;           // 画的总数
int m;           // 需要展出的画的数量
int d;           // 相邻展出画之间的最小距离要求

// 检查是否可以选择至少 m 幅价值 >= mid 的画
// 且相邻选择的画之间的距离至少为 d
bool check(int mid)
{
    int s = 0;              // 已选择的画的数量
    int last = -1e9;        // 上一次选择的画的位置

    // 贪心选择:从左到右遍历,能选就选
    for (int i = 1; i <= n; i++)
    {
        // 如果当前画的价值 >= mid 且与上一幅选中画的距离 >= d
        if (p[i].v >= mid && p[i].x - last >= d)
        {
            s++;                // 选择这幅画
            last = p[i].x;      // 更新最后选择的位置
        }
    }

    return s >= m;          // 是否选择了至少 m 幅画
}

int main()
{
    // 读取画的总数、需要展出的数量、最小距离要求
    scanf("%d%d%d", &n, &m, &d);

    int l = 0;              // 二分左边界
    int r = 0;              // 二分右边界
    int ans = 0;            // 最终答案

    // 输入每幅画的位置和价值
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &p[i].x, &p[i].v);
        r = max(r, p[i].v);     // 更新最大价值作为右边界
    }

    // 按位置升序排序
    sort(p + 1, p + n + 1, cmp);

    // 二分查找:最大可行的"华丽度"
    while (l <= r)
    {
        int mid = (l + r) / 2;      // 猜测的华丽度

        if (check(mid))             // 如果 mid 可行
        {
            ans = mid;              // 记录答案
            l = mid + 1;            // 尝试更大的华丽度
        }
        else
        {
            r = mid - 1;            // 尝试更小的华丽度
        }
    }

    // 输出结果
    if (ans == 0)
    {
        printf("%d\n", -1);         // 无法选出 m 幅画满足条件
    }
    else
    {
        printf("%d\n", ans);        // 输出最大华丽度
    }

    return 0;
}

【运行结果】

3 2 20
10 250
30 200
50 500
250
posted @ 2026-06-17 17:24  团爸讲算法  阅读(7)  评论(0)    收藏  举报