NYIST78-圈水池(凸包)

 

圈水池

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 4
描述
有一个牧场,牧场上有很多个供水装置,现在牧场的主人想要用篱笆把这些供水装置圈起来,以防止不是自己的牲畜来喝水,各个水池都标有各自的坐标,现在要你写一个程序利用最短的篱笆将这些供水装置圈起来!(篱笆足够多,并且长度可变)
输入
第一行输入的是N,代表用N组测试数据(1<=N<=10)
第二行输入的是m,代表本组测试数据共有m个供水装置(3<=m<=100)
接下来m行代表的是各个供水装置的横纵坐标
输出
输出各个篱笆经过各个供水装置的坐标点,并且按照x轴坐标值从小到大输出,如果x轴坐标值相同,再安照y轴坐标值从小到大输出
样例输入
1
4
0 0
1 1
2 3
3 0
样例输出
0 0
2 3
3 0

分析:直接就是凸包问题,要注意的是如果多个点同时在一条直线上,并且这条直线式凸包的边时,所有的点都要输出!

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define N 110
struct point{
    int x,y;
    double dis;
}p[N],res[N],temp;
int cmp2(const void *a, const void *b){
    struct point *A,*B;
    A=(struct point *)a;
    B=(struct point *)b;
    if(A->x!=B->x)
        return A->x-B->x;
    else
        return A->y-B->y;
}

double getdis(struct point a, struct point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

void find(int n){
    int i,k=0;
    temp=p[0];
    for(i=1;i<n;i++){
        if(p[i].y<temp.y||(p[i].y==temp.y&&p[i].x<temp.x)){
            temp=p[i];
            k=i;
        }
    }
    if(k!=0){
        temp=p[0];
        p[0]=p[k];
        p[k]=temp;
    }
    for(i=1,p[0].dis=0;i<n;i++){
        p[i].dis=getdis(p[0],p[i]);
    }
}
int check(struct point a, struct point b,struct point c){//叉乘 
    int x1,y1,x2,y2;
    x1=b.x-a.x;
    y1=b.y-a.y;
    x2=c.x-b.x;
    y2=c.y-b.y;
    return x1*y2-x2*y1;
}
int cmp(const void *a, const void *b) {//极角排序 
    struct point *A,*B;
    A=(struct point *)a;
    B=(struct point *)b;
    if((A->y-p[0].y)*(B->x-p[0].x)!=(A->x-p[0].x)*(B->y-p[0].y))
        return atan2(A->y-p[0].y,A->x-p[0].x)>atan2(B->y-p[0].y,B->x-p[0].x)?1:-1;
    else
        return A->dis>B->dis?1:-1;
}
int graham(int n) {
    int i,top;
    res[0]=p[0];
    res[1]=p[1];
    for(i=2,top=2;i<n;i++){//逆时针扫描,如果发现向右偏则需要退栈,如果要等号则同一直线上的点只算端点 
        while(top>=1&&check(res[top-2],res[top-1],p[i])<=0)
            top--;
        res[top++]=p[i];
    }
    return top;
}
int main(){
    int T,n,i,ans;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(i=0;i<n;i++){
            scanf("%d %d",&p[i].x,&p[i].y);
        }
        find(n);//找到最下左位置的点,并将其放到p[0]位置 
        qsort(p+1,n-1,sizeof(p[0]),cmp);//按照极角排序,如果角度相同则按照与基点距离大小排序 
        ans=graham(n);
		qsort(res,ans,sizeof(res[0]),cmp2);
        for(i=0;i<ans;i++){//按照逆时针顺序输出凸包上的点 
            printf("%d %d\n",res[i].x,res[i].y);
        }
    }
    system("pause");
    return 0;
}

/*
11
2 0
4 0
0 1
1 1
3 1
2 2
5 2
1 3
4 3
2 4
4 4


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

*/ 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值