区域赛选拔题--Moon Game--计算几何&&判断平面点集组成的凸边行的个数

【题意】给出平面的一系列的点的坐标,求其中组成凸边形的个数。

链接:click here~~

简单的计算几何题,熟悉模板的话,很快就能敲出来,只怪自己这番方面的题练的不够多,磨了一两个小时才A掉,

代码:适合(数据不大的情况下)。

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define eps 1e-6
struct xy
{
    int x;
    int y;
} map[100];
int fun(int a,int b,int c)
{
    int t;
    t=(map[a].x-map[c].x)*(map[b].y-map[c].y)-(map[b].x-map[c].x)*(map[a].y-map[c].y);
    return t;
}
double area(xy a, xy b, xy c)
{
    return(fabs((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))/2);
}
bool check(xy a, xy b, xy c,xy d)
{
    if(fabs(area(b,c,d)-area(a,b,c)-area(a,c,d)-area(a,b,d))<eps)
        return false;
    return true;
}
int main()
{
    int a,b,t,i,j,n,sb=1;
    scanf("%d",&t);
    while(t--)
    {
        int ans=0;
        scanf("%d",&n);
        for(i=0; i<n; i++)
        {
            scanf("%d %d",&map[i].x,&map[i].y);
        }
//        for(i=0;i<n;i++)
//        if(t>=0)
//        {
//            ans++;
//        }
        if(n<4)//注意小于四,直接判断
            printf("Case %d: %d", sb, ans);
        else
        {
            for(i = 0; i < n; i++)
            for(j = i+1; j < n; j++)
            for(a = j+1; a < n; a++)
            for(b = a+1; b < n; b++)
            if(check(map[i],map[j],map[a],map[b])&&check(map[j],map[i],map[a],map[b])
            &&check(map[a],map[i],map[j],map[b])&&check(map[b],map[i],map[j],map[a]))
            {
            ans++;
            }
            //printf("%d\n",ans);
        }
        printf("Case %d: %d\n",sb++,ans);
    }
    return 0;
}
HDU3629(数据比较大(4~700),还没做,不过当作模板了,用上面方法会超时),链接: click here~~

http://blog.himdd.com/archives/1162

代码:

//Problem : 3629 ( Convex )     Judge Status : Accepted
//RunId : 4422243    Language : C++    Author : himdd
//Code Render Status : Rendered By HDOJ C++ Code Render Version 0.01 Beta
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
const double pi = std::acos(-1.0);
struct point {
    int x, y;
};

point pt[1500];
double ang[1500];

long long C2(int x) {
    return (long long)x * (x - 1) / 2;
}

long long solve(int n) {
    int i, j, k, tpl, tpr, tn, tpi;
    point tp;
    long long ans = 0 ;

    for(i = 0; i < n; i ++) {
        tp = pt[0];
        pt[0] = pt[i];
        pt[i] = tp;
        for(j = 1; j < n; j ++) {
            ang[j] = std::atan2((double)
pt[j].y - pt[0].y, (double)pt[j].x - pt[0].x);
        }
        std::sort(ang + 1, ang + n);
        for(j = 1; j < n; j ++)
            ang[n + j - 1] = ang[j] + 2 * pi;
        tpi = 1;
        for(j = 1; j < n; j ++) {
            for(; ang[tpi] < ang[j] + pi; tpi ++);
            tpl = tpi - j - 1;
            tpr = n - 2 - tpl;
            ans += C2(tpl) + C2(tpr) - (long long)tpl * tpr;
        }
    }

    return ans / 4;
}

int main() {
    int z, i, n;
    ::scanf("%d", &z);
    while(z --) {
        scanf("%d", &n);
        for(i = 0; i < n; i ++)
            scanf("%d %d", &pt[i].x, &pt[i].y);

        printf("%I64d\n", solve(n));
    }
}
总结一下常用的几种方法:

(1)叉积&&叉乘(常用):

利用以当前顶点为中心的矢量叉乘或者计算三角形的有符号面积判断多边形的方向以及当前顶点的凹凸性。

假设当前连续的三个顶点分别是P1,P2,P3。计算向量P1P2,P2P3的叉乘,也可以计算三角形P1P2P3的面积,得到的结果如果大于0,则表示P3点在线段P1和P2的左侧,多边形的顶点是逆时针序列。然后依次计算下一个前后所组成向量的叉乘,如果在计算时,出现负值,则此多边形时凹多边形,如果所有顶点计算完毕,其结果都是大于0,则多边形时凸多边形。

(2)角度:

判断每个顶点所对应的内角是否小于180度,如果小于180度,则是凸的,如果大于180度,则是凹多边形。

(3)凸包法:

这种方法首先计算这个多边形的凸包,关于凸包的定义在此不再赘述,首先可以肯定的是凸包肯定是一个凸多边形。如果计算出来的凸多边形和原始多边形的点数一样多,那就说明此多边形时凸多边形,否则就是凹多边形。

(4)辛普森面积法利用待判别的顶点以及前后两个顶点所组成的三角形,利用辛普森公式计算其面积,如果此三角形面积与整个多边形面积符号相同,那么这个顶点是凸的;如果此三角形面积与整个多边形面积符号不同,那么这个顶点是凹的,即整个多边形也是凹多边形。

判断给出的点的坐标能否组成一个凸边形,

代码:

链接:hdu 2108   click here

#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<dequq>
#include<stack>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define mem(a,b) memset(a,b,sizeof(a))
int dir[4][2]= {{0,-1},{-1,0},{0,1},{1,0}};
const double eps = 1e-6;
const double Pi = acos(-1.0);
struct xy
{
    int x;
    int y;
} d[1000];
int fun(int a,int b,int c)
{
    int t;
    //公式:s=(x1-x3)*(y2-y3)-(x2-x3)*(y1-y3)
    //当s>0时,p1,p2,p3三个点呈逆时针
    //当s<0时,p1,p2,p3三个点呈顺时针
    t=(d[a].x-d[c].x)*(d[b].y-d[c].y)-(d[b].x-d[c].x)*(d[a].y-d[c].y);
    return t;
}
int main()
{
    int i,t,n;
    while(scanf("%d",&n),n)
    {
        for(i=0; i<n; i++)
        {
            scanf("%d %d",&d[i].x,&d[i].y);
        }
        for(i=0; i<n; i++)
        {
            //模n是因为当i=n-1的时候n+1,n+2会超出数据范围,所以从头开始为最后一个点和第一二个点判断直线的走向
            t=fun(i%n,(i+1)%n,(i+2)%n);
            if(t<0)break;
        }
        if(t>=0)
            printf("convex\n");
        else
            printf("concave\n");
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值