7-15 水果忍者 (30 分)
2010年风靡全球的“水果忍者”游戏,想必大家肯定都玩过吧?(没玩过也没关系啦~)在游戏当中,画面里会随机地弹射出一系列的水果与炸弹,玩家尽可能砍掉所有的水果而避免砍中炸弹,就可以完成游戏规定的任务。如果玩家可以一刀砍下画面当中一连串的水果,则会有额外的奖励,如图1所示。
图 1
现在假如你是“水果忍者”游戏的玩家,你要做的一件事情就是,将画面当中的水果一刀砍下。这个问题看上去有些复杂,让我们把问题简化一些。我们将游戏世界想象成一个二维的平面。游戏当中的每个水果被简化成一条一条的垂直于水平线的竖直线段。而一刀砍下我们也仅考虑成能否找到一条直线,使之可以穿过所有代表水果的线段。
图 2
如图2所示,其中绿色的垂直线段表示的就是一个一个的水果;灰色的虚线即表示穿过所有线段的某一条直线。可以从上图当中看出,对于这样一组线段的排列,我们是可以找到一刀切开所有水果的方案的。
另外,我们约定,如果某条直线恰好穿过了线段的端点也表示它砍中了这个线段所表示的水果。假如你是这样一个功能的开发者,你要如何来找到一条穿过它们的直线呢?
输入格式:
输入在第一行给出一个正整数N
(≤104),表示水果的个数。随后N
行,每行给出三个整数x、y1、y2,其间以空格分隔,表示一条端点为(x,y1)和(x,y2)的水果,其中y1>y2。注意:给出的水果输入集合一定存在一条可以将其全部穿过的直线,不需考虑不存在的情况。坐标为区间 [−106,106) 内的整数。
输出格式:
在一行中输出穿过所有线段的直线上具有整数坐标的任意两点p1(x1,y1)和p2(x2,y2),格式为 x1y1x2y2。注意:本题答案不唯一,由特殊裁判程序判定,但一定存在四个坐标全是整数的解。
输入样例:
5
-30 -52 -84
38 22 -49
-99 -22 -99
48 59 -18
-36 -50 -72
输出样例:
-99 -99 -30 -52
对于所有线段的上端点求一个下凸,下端点求一个上凸,因为题目说一定存在一个可行解,则可行解一定在这两个凸包之间。分为两种情况
一、对于上端点的下凸中,存在一条边使所有的下端点都在它的下面,那么这条边就是一个可行解。
二、否则就在下端点中找一条边使所有的上端点都在这条边的上面。
#include "bits/stdc++.h" using namespace std; const int maxn = 1e4 + 1000; const int inf = 0x3f3f3f3f; int sup[maxn], sdown[maxn]; int n; struct point { int x, y; } up[maxn], down[maxn]; bool cmp(point a, point b) { if (a.x != b.x) return a.x < b.x; return a.y < b.y; } bool checkup(point a, point b, point c) {//不满足下凸返回true return 1ll * (b.x - a.x) * (c.y - b.y) - 1ll * (c.x - b.x) * (b.y - a.y) < 0; } bool checkdown(point a, point b, point c) {//不满足上凸返回true return 1ll * (b.x - a.x) * (c.y - b.y) - 1ll * (c.x - b.x) * (b.y - a.y) > 0; } int main() { cin >> n; int a, b, c; for (int i = 0; i < n; i++) { cin >> a >> b >> c; up[i].x = down[i].x = a; up[i].y = b; down[i].y = c; } sort(up, up + n, cmp); sort(down, down + n, cmp); int dx = 0; int to = n; for (int i = 1; i < n; i++) { if (up[i].x == up[dx].x) { up[i].x = inf; up[dx].y = min(up[i].y, up[dx].y); to--; } else { dx = i; } } dx = 0; for (int i = 1; i < n; i++) { if (down[i].x == down[dx].x) { down[i].x = inf; down[dx].y = max(down[i].y, down[dx].y); } else { dx = i; } } sort(up, up + n, cmp); sort(down, down + n, cmp); if (to == 1) { printf("%d %d %d %d ", up[0].x, up[0].y, down[0].x, down[0].y); return 0; } int upos = 0, dpos = 0; for (int i = 0; i < to; i++) { while (upos >= 2 && checkup(up[sup[upos - 2]], up[sup[upos - 1]], up[i])) upos--; sup[upos++] = i; while (dpos >= 2 && checkdown(down[sdown[dpos - 2]], down[sdown[dpos - 1]], down[i])) dpos--; sdown[dpos++] = i; } int i, j; point ansl, ansr; for (i = 0; i < upos - 1; i++) { for (j = 0; j < dpos; j++) { if (checkup(up[sup[i]], down[sdown[j]], up[sup[i + 1]])) break; } if (j == dpos) break; } if (i == upos - 1) { for (i = 0; i < dpos - 1; i++) { for (j = 0; j < upos; j++) { if (checkdown(down[sdown[i]], up[sup[j]], down[sdown[i + 1]])) break; } if (j == upos) break; } ansl = down[sdown[i]]; ansr = down[sdown[i + 1]]; } else { ansl = up[sup[i]]; ansr = up[sup[i + 1]]; } printf("%d %d %d %d ", ansl.x, ansl.y, ansr.x, ansr.y); return 0; }