题解:学而思编程 展览会
【题目来源】
学而思编程:展览会
【题目描述】
美术馆有 \(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
【核心思想】
-
问题分析:给定 \(n\) 幅画,每幅画有位置 \(x_i\) 和价值 \(v_i\),需要选出 \(m\) 幅画满足相邻画之间的距离 \(\geq D\),使得选出画中价值最低的那幅画的"华丽度"最大化。这是一个最大化最小值问题,具有单调性:如果华丽度 \(mid\) 可行,则所有小于 \(mid\) 的值也可行。
-
算法选择:
- 整数二分(二分答案):二分查找最大可行的"华丽度"
- 贪心验证:对于给定的华丽度 \(mid\),使用贪心策略判断是否能选出至少 \(m\) 幅价值 \(\geq mid\) 且间距满足要求的画
- 排序预处理:按位置排序以便贪心选择
-
关键步骤:
- 输入与初始化:
- 读入 \(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\)
- 输入与初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log V + n \log n)\),排序 \(O(n \log n)\),二分 \(O(\log V)\) 次,每次验证 \(O(n)\),其中 \(V\) 为最大价值
- 空间复杂度:\(O(n)\),存储画的信息
-
二分答案与贪心验证的核心思想:
- 二分答案技巧:将"求最大/最小满足条件的值"转化为"判断某个值是否满足条件",利用单调性进行二分
- 贪心选择策略:从左到右能选就选,保证在间距约束下选出最多的画,这是最优的
- 验证的单调性:若华丽度 \(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
浙公网安备 33010602011771号