1 条题解
-
0
题目理解
给定一个包含 个整数的列表 。
我们需要从中选出 个元素作为四个点的坐标,这四个点构成一个边与坐标轴平行的矩形。
矩形的四个顶点为:其中 ,。
目标是使矩形面积 最大。
若无法构造,输出 。
关键观察
. 每个坐标值()在矩形中出现的次数:
- 若四个值互不相同,每个值出现恰好 次(因为每个 对应两个 ,每个 对应两个 )。
- 若 ,则该值出现在三个点中:、、,因此至少需要出现 次。
- 若 且 ,则每个值出现 次,但此时矩形退化为线段(面积 )。
- 若 且 ,同理。
. 为了最大化面积,我们应让 和 尽量远离, 和 尽量远离。
因此最优的 应该是候选值中最小和最大的,最优的 应该是候选值中第二小和第二大(若互不相同且出现次数足够)。. 候选值:在原列表中出现次数 的数。因为每个值在矩形中至少出现 次(除非特殊重复情况,但重复会减小面积)。
算法步骤
. 统计每个值的出现次数 。 . 收集所有出现次数 的值到 数组。 . 若 ,则无法构造矩形(至少需要两个不同的 和两个不同的 ,但可能通过重复值实现,不过面积 ,且需要出现次数足够,这里简化处理,直接判 )。 . 对 排序。 . 设 ,(这样 的差最大)。 . 尝试找出最优的 (,且出现次数足够):
- 优先尝试 $$(cand[1], cand[m-2])$$(若 ),因为这是次小和次大,差尽可能大且与 不同。
- 若 与
(cand[1], cand[m-2])有冲突,则尝试其他组合:- 若 出现次数 ,则 也可以取 (此时 与 重合,需要检查次数)。
- 尝试 $$(cand[0], cand[m-2])$$ 和 $$(cand[1], cand[m-1])$$,确保不违反出现次数。
- 若 ,则只有两个候选值,此时只能取 或 (退化),需检查出现次数 或 。
. 若找不到合法的 ,输出
NO。 . 否则输出YES和四个点的坐标(按 顺序,任意顺序均可)。
正确性说明
- 我们固定 为全局最小和最大,因为改变 对只会减小 ,从而可能减小面积(除非 差能大幅增加,但 差受限于候选值范围,不会超过全局次大减次小,而全局次大减次小 全局最大减最小,所以 取最大是最优的)。
- 的候选我们尝试了可能的最大差值组合,确保面积最大。
- 特殊情况()单独处理,避免遗漏。
时间复杂度
- 统计频次:(使用
map)或 (使用unordered_map,但值范围大,可能冲突)。 - 排序:, 为不同值的数量,。
- 总复杂度 , 总和 ,足够快。
完整代码(标程)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; vector<long long> a(n); map<long long, int> cnt; for (int i = 0; i < n; i++) { cin >> a[i]; cnt[a[i]]++; } vector<long long> cand; for (auto& [val, c] : cnt) { if (c >= 2) cand.push_back(val); } if (cand.size() < 2) { cout << "NO\n"; continue; } sort(cand.begin(), cand.end()); int m = cand.size(); long long x1 = cand[0], x2 = cand[m-1]; long long y1, y2; long long dy = -1; // try (cand[1], cand[m-2]) if exists if (m >= 4) { y1 = cand[1], y2 = cand[m-2]; dy = y2 - y1; } // try (cand[0], cand[m-1]) if not same as x1,x2 or enough count if (x1 != cand[0] || x2 != cand[m-1]) { // actually they are same as x1,x2, so need cnt >=3 if (cnt[x1] >= 3 && cnt[x2] >= 3) { long long ndy = x2 - x1; if (ndy > dy) { dy = ndy; y1 = x1; y2 = x2; } } } // try (cand[0], cand[m-2]) and (cand[1], cand[m-1]) etc for safety if (m >= 3) { if (cnt[cand[0]] >= 2 && cnt[cand[m-2]] >= 2 && (cand[0] != x1 || cand[m-2] != x2)) { long long ndy = cand[m-2] - cand[0]; if (ndy > dy) { dy = ndy; y1 = cand[0]; y2 = cand[m-2]; } } if (cnt[cand[1]] >= 2 && cnt[cand[m-1]] >= 2 && (cand[1] != x1 || cand[m-1] != x2)) { long long ndy = cand[m-1] - cand[1]; if (ndy > dy) { dy = ndy; y1 = cand[1]; y2 = cand[m-1]; } } } // if m==2 if (m == 2) { if (cnt[x1] >= 4) { dy = 0; y1 = x1; y2 = x1; } else if (cnt[x1] >= 3 && cnt[x2] >= 3) { dy = 0; y1 = x1; y2 = x2; } } if (dy < 0) { cout << "NO\n"; continue; } cout << "YES\n"; // output points: (x1,y1), (x1,y2), (x2,y1), (x2,y2) cout << x1 << " " << y1 << " "; cout << x1 << " " << y2 << " "; cout << x2 << " " << y1 << " "; cout << x2 << " " << y2 << "\n"; } return 0; }
示例验证
样例 1
输入:
3 16 -5 1 1 2 2 3 3 4 4 5 5 6 6 7 7 10输出:
YES 1 2 1 7 6 2 6 7面积 = ,最大。
样例 2
输入:
8 0 0 -1 2 2 1 1 3输出:
NO无法构成矩形(候选值 出现次数均 ,但最小 ,最大 , 候选次小 ,次大 ,差 ,面积 ,但检查出现次数: 出现 次,不可用,所以无合法矩形)。
样例 3
输入:
8 0 0 0 0 0 5 0 5输出:
YES 0 0 0 5 0 0 0 5退化矩形,面积 。
总结
- 核心:选择最小和最大的数作为 ,选择次小和次大的数作为 (若可用)。
- 处理边界情况()及出现次数不足的情况。
- 输出任意顺序的四个顶点坐标。
- 1
信息
- ID
- 6676
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者