PTA L3-012 水果忍者(上下凸包)

2010年风靡全球的“水果忍者”游戏,想必大家肯定都玩过吧?(没玩过也没关系啦~)在游戏当中,画面里会随机地弹射出一系列的水果与炸弹,玩家尽可能砍掉所有的水果而避免砍中炸弹,就可以完成游戏规定的任务。如果玩家可以一刀砍下画面当中一连串的水果,则会有额外的奖励,如图1所示
PTA L3-012 水果忍者(上下凸包)_第1张图片
图 1

现在假如你是“水果忍者”游戏的玩家,你要做的一件事情就是,将画面当中的水果一刀砍下。这个问题看上去有些复杂,让我们把问题简化一些。我们将游戏世界想象成一个二维的平面。游戏当中的每个水果被简化成一条一条的垂直于水平线的竖直线段。而一刀砍下我们也仅考虑成能否找到一条直线,使之可以穿过所有代表水果的线段。
PTA L3-012 水果忍者(上下凸包)_第2张图片
图 2

如图2所示,其中绿色的垂直线段表示的就是一个一个的水果;灰色的虚线即表示穿过所有线段的某一条直线。可以从上图当中看出,对于这样一组线段的排列,我们是可以找到一刀切开所有水果的方案的。

另外,我们约定,如果某条直线恰好穿过了线段的端点也表示它砍中了这个线段所表示的水果。假如你是这样一个功能的开发者,你要如何来找到一条穿过它们的直线呢?

输入格式:
输入在第一行给出一个正整数N(≤10​4​​),表示水果的个数。随后N行,每行给出三个整数x、y​1​​、y​2​​,其间以空格分隔,表示一条端点为(x,y​1​​)和(x,y​2​​)的水果,其中y​1​​>y​2​​。注意:给出的水果输入集合一定存在一条可以将其全部穿过的直线,不需考虑不存在的情况。坐标为区间 [−10​6​​,10​6​​) 内的整数。

输出格式:
在一行中输出穿过所有线段的直线上具有整数坐标的任意两点p​1​​(x​1​​,y​1​​)和p​2​​(x​2​​,y​2​​),格式为 x​1​​y​1​​x​2​​y​2​​。注意:本题答案不唯一,由特殊裁判程序判定,但一定存在四个坐标全是整数的解。

输入样例:
5
-30 -52 -84
38 22 -49
-99 -22 -99
48 59 -18
-36 -50 -72
输出样例:
-99 -99 -30 -52
对于所有线段的上端点求一个下凸,下端点求一个上凸,因为题目说一定存在一个可行解,则可行解一定在这两个凸包之间。分为两种情况
一、所有的上端点都在下端点凸包一边的上侧,则这条边就是一个可行解。
PTA L3-012 水果忍者(上下凸包)_第3张图片
二、对于上端点凸包的一条边,所有下端点凸包上的点都等于它,那么这条边就是一个可行解。
PTA L3-012 水果忍者(上下凸包)_第4张图片

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int INF=0x3f3f3f3f;
const int MOD=998244353;
#define rep(i,a,b) for(int i=a;i<=b;i++)
int n;
map<int,int>ma;
int cnt;
struct node{
    int x,y;
    bool operator < (const node &a) const{
            return x<a.x;
    }
}up[N],down[N];
int topup,topdown;
int quup[N],qudown[N];
bool cossup(node o,node a,node b){//建立下凸包
   return 1ll*(a.y-o.y)*(b.x-o.x)-1ll*(b.y-o.y)*(a.x-o.x)>0;
}
bool cossdown(node o,node a,node b){//建立上图包
    return 1ll*(a.y-o.y)*(b.x-o.x)-1ll*(b.y-o.y)*(a.x-o.x)<0;;
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
    scanf("%d",&n);
    for(int i=0;i<n;i++){int x,y1,y2;
    scanf("%d%d%d",&x,&y1,&y2);
        if(ma[x]==0){
            up[cnt].x=down[cnt].x=x;
            up[cnt].y=y1,down[cnt++].y=y2;
            ma[x]=cnt;
        }else{
            up[ma[x]-1].y=min(y1,up[ma[x]-1].y);
            down[ma[x]-1].y=max(y2,down[ma[x]-1].y);
        }
    }
    if(cnt==1){
      printf("%d %d %d %d\n",up[0].x,up[0].y,down[0].x,down[0].y)  ;
      return 0;
    }
    sort(up,up+cnt);
    sort(down,down+cnt);
    qudown[1]=quup[1]=0;
    qudown[2]=quup[2]=1;
    topdown=topup=2;
    node ans1,ans2;
    for(int i=2;i<cnt;i++){
        while(topup>=2&&cossup(up[quup[topup-1]],up[quup[topup]],up[i])) topup--;
        quup[++topup]=i;
        while(topdown>=2&&cossdown(down[qudown[topdown-1]],down[qudown[topdown]],down[i])) topdown--;
        qudown[++topdown]=i;
    }
    int i,j;
    for(i=1;i<topup;i++){
        for(j=1;j<=topdown;j++){
            if(cossup(up[quup[i]],down[qudown[j]],up[quup[i+1]])) break;
        }
        if(j>topdown) break;
    }
    if(i==topup){
        for(i=1;i<topdown;i++){
            for(j=1;j<=topup;j++){
                if(cossdown(down[qudown[i]],up[quup[j]],down[qudown[i+1]])) break;
            }
            if(j>topup) break;
        }
        ans1=down[qudown[i]];
        ans2=down[qudown[i+1]];
    }else{
        ans1=up[quup[i]];ans2=up[quup[i+1]];
    }
    printf("%d %d %d %d\n",ans1.x,ans1.y,ans2.x,ans2.y);
	return 0;
}

如果不太理解凸包的可以点(>_<)

你可能感兴趣的:(凸包)