HDU 4946 Area of Mushroom (凸包)

Area of Mushroom

问题描述

**中学有一个很大的操场,上面有n名同学(编号1到n)。我们可以看做一个无限大的平面上有n个坐标点。同学们跑步速度有快有慢。对于操场上的任何一点,如果一个学生能比其他学生都先到达(跑直线,没有人比他先到或同时到),那么这个点就被他占领。 请你计算,哪些同学能占领无穷多个点。

输入格式

输入含有多组数据(<=8组),对于每组数据:
第一行一个整数n。

接下来n行,每行三个整数x,y,v,按1到n的顺序依次描述一个同学的位置坐标(x,y)和他的跑步速度。

输入以单独一行,一个数字0作为结束。

输出格式

对于每组数据,输出一行,n个数字,对应1到n号学生的情况。如果该学生能占用无限多个点,输出1,否则输出0。

样例输入 1

3
0 0 3
1 1 2
2 2 1
0

样例输出 1

100

样例输入 2

3
0 0 2
1 1 1
2 0 2
4
1 1 3
1 1 2
2 2 2
3 3 1
0

样例输出 2

101
1000

提示

对于20%的数据: 1<=n<=5
对于100%的数据:1<=n<=500 0<=|x|,|y|,v<=10^4


首先注意到只有速度最大的人才有可能占有无限的面积,这个比较显然,推式子的话会发现如果速度不相等,那么最终速度小的占有的会是一个圆。

因此只考虑速度最大的人,那么只有凸包上的人才能占有无限的面积,这个也比较容易脑补。因此只需要求出所有速度最大的人的凸包。

但是注意到两种特殊情况。
一是如果所有人速度都为0,那么答案显然都为0。
二是可能存在两个人的坐标相同,速度也相同,此时根据题意,两个人占有的面积都是0,需要特判,但是注意求凸包的时候,所有坐标速度都相同的人只能加一个且必须加一个,加多了会导致求凸包算法出问题,因为本题中凸包上三点共线是要保留的,因此有重点就会使得该被弹掉的点弹不掉。

复杂度 O(nlogn) O ( n log ⁡ n ) ,事实上是可以推导一番用半平面交的,但精度好像有点问题?


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;
const double eps=1e-12;
struct node{double x,y;int id;}P[N],T[N],S[N];
bool operator<(node a,node b)
{
    if(a.x==b.x)return a.y>b.y;
    return a.x<b.x;
}
node operator-(node a,node b){return (node){a.x-b.x,a.y-b.y};}
double operator*(node a,node b){return a.x*b.y-a.y*b.x;}
int n,v[N],top,m,tot;
bool mark[N],rem[N];
bool find(int x)
{
    bool f=0;
    for(int i=1;i<=n;i++)
    {
        if(i==x)continue;
        if(P[i].x!=P[x].x)continue;
        if(P[i].y!=P[x].y)continue;
        if(v[i]!=v[x])continue;
        f=1;rem[i]=1;
    }
    return f;
}
bool judge(node a,node b,node c)
{return (b-a)*(c-a)<0;}
void GetTB()
{
    int i,j,k;top=0;
    sort(T+1,T+tot+1);
    for(i=1;i<=tot;i++)
    {
        while(top>1&&judge(S[top-1],S[top],T[i]))top--;
        S[++top]=T[i];
    }
    k=--top;
    for(i=tot;i>=1;i--)
    {
        while(top>k+1&&judge(S[top-1],S[top],T[i]))top--;
        S[++top]=T[i];
    }
    for(i=1;i<=top;i++)mark[S[i].id]=1;
}
int main()
{
    int i,j,k,Max;
    while(scanf("%d",&n))
    {
        if(n==0)break;Max=0;tot=0;
        memset(mark,0,sizeof(mark));
        memset(rem,0,sizeof(rem));
        for(i=1;i<=n;i++)scanf("%lf%lf%d",&P[i].x,&P[i].y,&v[i]);
        for(i=1;i<=n;i++)Max=max(Max,v[i]),P[i].id=i;
        if(Max==0)
        {
            for(i=1;i<=n;i++)putchar('0');
            puts("");continue;
        }
        for(i=1;i<=n;i++)if(v[i]==Max&&!rem[i])T[++tot]=P[i],find(i);
        GetTB();
        for(i=1;i<=n;i++)mark[i]&&!find(i)?putchar('1'):putchar('0');
        puts("");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值