【计蒜客】红黑点

32 篇文章 0 订阅

题目


在二维平面有 n 个红点和 m 个黑点,已知这些点的坐标,这些点不存在三点共线的情况。从这些红点中任意取出三个点,可以构成 n * ( n - 1 ) * ( n - 2 ) / 6 个三角形,求这些三角形中包含黑点的数量为奇数的三角形的数量。

输入格式
输入包含多组测试数据,对于每组测试数据:
第一行包含两个整数 n m ( 3 ≤ n ≤ 100 ; 0 ≤ m ≤ 1000 ) 。
接下来 n 行每行包含两个整数,表示 n 个红点的坐标。
接下来 m 行每行包含两个整数,表示 m 个黑点的坐标。
输入保证所有的坐标值的绝对值小于等于 100000 。

输出格式
对于每组测试数据,输出 “Case c: ans” ,其中 c 表示测试数据编号, ans 表示包含奇数个黑点的三角形的数量。
样例输入
4 5
0 0
10 0
0 10
10 10
1 2
3 4
6 2
9 5
6 7
样例输出
Case 1: 2


思路


根据题目所说我们将所有的红色点都将构成三角形并且我们判断黑色的点是否在三角形内部,然后我们转化如何判断点在三角形内部;
根据数学知识叉乘发来判断点是否在三角形内部

叉乘法


沿着三角形的边按顺时针方向走,判断该点是否在每条边的右边(这可以通过叉乘判断),如果该点在每条边的右边,则在三角形内,否则在三角形外。这个算法只用到了三次叉乘,没有除法运算和三角函数、开根号等运算,所以效率很高,而且精度很高(没有浮点误差)。

即(x1*y2-x2*y1)

设三角形三点A(x1,y1)B(x2,y2)C(x3,y3),已知点M(x,y)

1,先求出三个向量MA,MB,MC.

2,计算MA X MB,MB X MC,MC X MA (X表叉乘)

3,如果此三组的向量叉乘的结果都是同号的(或都正,或都负),即方向相同的,则说明点M在三角形每条边的同侧,即内部。否则必在外部!
具体示例:
A(0,3) B(0,0) C(3,0)

M(1,1)
MA (1,-2)
MB (1,1)
MC (-2,1)

MA X MB = 3
MB X MC = 3
MC X MA = 3

N(3,3)
NA (3,0)
NB (3,3)
NC (0,3)

NA X NB = 9
NB X NC = 9
NC X NA = -9

根据以上规则可判断M点在内 N点在外

下面引入代码


代码


#include<iostream>
using namespace std;
#define maxn 1000
static int cc = 0;
int n, m;
struct point  //点创建
{
    float x, y;
}p[maxn], b[maxn];

bool IsInPology(point xx, point yy, point zz, point b)//判断点是否在内部 x,y,z分别表示三个点,b[]表示黑色点的数据
{
    point bx, by, bz;
    bx.x = b.x - xx.x;
    bx.y = b.y - xx.y;

    by.x = b.x - yy.x;
    by.y = b.y - yy.y;

    bz.x = b.x - zz.x;
    bz.y = b.y - zz.y;

    float t1, t2, t3;//叉乘法
    t1 = bx.x*by.y - bx.y*by.x;
    t2 = by.x*bz.y - by.y*bz.x;
    t3 = bz.x*bx.y - bz.y*bx.x;
    if ((t1 >= 0 && t2 >= 0 && t3 >= 0) || (t1 <= 0 && t2 <= 0 && t3 <= 0))return true;
    else
        return false;
}
//是否具有奇数个黑色的点
bool IsOd(point x, point y, point z, point b[])
{
    int cnt = 0;//计数器统计黑色点的数量
    for (int i = 0; i<m; i++)
    {
        if (IsInPology(x, y, z, b[i]) == true)cnt++;
    }
    if (cnt % 2 == 0)return false;
    return true;
}
int main()
{
    cin >> n >> m;
    //红色的点  n


    for (int i = 0; i<n; i++)
    {
        cin >> p[i].x >> p[i].y;
    }
    //黑色的点  m
    for (int i = 0; i<m; i++)
    {
        cin >> b[i].x >> b[i].y;
    }
    //取三个点
    int numP = n, cnt = 0;
    for (int i = 0; i < n - 2; i++)
    {
        for (int j = i + 1; j < n - 1; j++)
        {
            for (int k = j + 1; k < n; k++)
            {
                if (IsOd(p[i], p[j], p[k], b) == true)cnt++;
            }
        }
    }
    cout << "Case "<<++cc<<": " << cnt << endl;
    return 0;
}

转载时,请注明出处,谢谢合作

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值