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
算法思路:Graham's Scan法求凸包(这题要求上凸和下凸)。构成凸包的线必有一条以上满足条件(这里没总结出来为什么,就画了个图感觉是这个样子。。。)
#include<stdio.h>
#include<algorithm>
using namespace std;
int N;
struct Point
{
int x, y;
}top[10000], down[10000];
int stop[10000], sdown[10000];
bool judgeT(Point a, Point b, Point c) {
return 1ll*(b.y - a.y)*(c.x - b.x) > 1ll*(b.x - a.x)*(c.y - b.y);
}
bool judgeD(Point a, Point b, Point c) {
return 1ll*(b.y - a.y)*(c.x - b.x) < 1ll*(b.x - a.x)*(c.y - b.y);
}
int compare(const void * a, const void* b) {
Point *ap = (Point*)(a), *bp = (Point*)(b);
if (ap->x < bp->x || (ap->x == bp->x&&ap->y < bp->y)) return -1;
return 1;
}
int main() {
scanf("%d", &N);
int i;
int x, y1, y2;
for (i = 0; i<N; i++) {
scanf("%d%d%d", &x, &y1, &y2);
top[i].x = down[i].x = x;
top[i].y = y1;
down[i].y = y2;
}
qsort(top, N,sizeof(Point), compare);
qsort(down, N, sizeof(Point), compare);
int xp = 0, nt = N;
for (i = 1; i < N; i++) {
if (top[i].x == top[xp].x) {
top[i].x = 100000000;
top[xp].y = min(top[i].y, top[xp].y);
nt--;
}
else xp = i;
}
xp = 0;
for (i = 1; i < N; i++) {
if (down[i].x == down[xp].x) {
down[i].x = 100000000;
down[xp].y = max(down[i].y, down[xp].y);
}
else xp = i;
}
qsort(top, N, sizeof(Point), compare);
qsort(down, N, sizeof(Point), compare);
N = nt;
if (N == 1) {
printf("%d %d %d %d\n", x, y1, x, y2);
return 0;
}
int tpre = 0, dpre = 0;
for (i = 0; i < N; i++) {
while (tpre >= 2 && judgeT(top[stop[tpre - 2]], top[stop[tpre - 1]], top[i])) tpre--;
stop[tpre++] = i;
while (dpre >= 2 && judgeD(down[sdown[dpre - 2]], down[sdown[dpre - 1]], down[i])) dpre--;
sdown[dpre++] = i;
}
int j;
for (i = 0; i < tpre - 1; i++) {
for (j = 0; j < dpre; j++)
if (judgeT(top[stop[i]], down[sdown[j]], top[stop[i + 1]])) break;
if (j == dpre)
break;
}
Point left, right;
if (i == tpre - 1) {
for (i = 0; i < dpre - 1; i++) {
for (j = 0; j < tpre; j++)
if (judgeD(down[sdown[i]], top[stop[j]], down[sdown[i + 1]])) break;
if (j == tpre)
break;
}
left = down[sdown[i]];
right = down[sdown[i + 1]];
}
else {
left = top[stop[i]];
right = top[stop[i + 1]];
}
printf("%d %d %d %d\n", left.x, left.y, right.x, right.y);
return 0;
}