洛谷 P1355 神秘大三角

题面

判断一个点与已知三角形的位置关系。(顶点上,边上,内部,外部)

分析

经过分析发现这个给定点与三角形三顶点连接的向量之间的外积有特点。
设这个给定的点是 P,三角形顶点 ABC(给出的顺序任意,也能保证ABC肯定是顺时针或逆时针的)
于是构造 P A ⃗ , P B ⃗ , P C ⃗ \vec{PA},\vec{PB},\vec{PC} PA ,PB ,PC
PA,PB,PC 的顺序肯定是满足单调方向的,由此想到右手定则,可以来研究他们之间按顺序外积的结果的特点。
P A ⃗ × P B ⃗ , P B ⃗ × P C ⃗ , P C ⃗ × P A ⃗ \vec{PA}×\vec{PB},\vec{PB}×\vec{PC},\vec{PC}×\vec{PA} PA ×PB ,PB ×PC ,PC ×PA



这种情况,以上三个外积都是正的,交换BC后,则三个外积都是负的。
三个同号就是这个的特点。



P与A重合,此时PA为零向量,外积的结果有两个值为 0。
更换其他的顶点重合也是这样。
于是这种情况的特点。




这两种很相似,在外积层面上,都是有一个0。
而前一种在边上:两个非0的外积同号。
后一种在外部与某条边共线:两个非0的外积异号。

这是一种特点,还有一种,可以用向量内积判定。判定两个外积为0的向量的内积是正是负,显然第一种是负的,第二种是正的。



在外部,且不与某条边共线,根据相乘顺序能看出来为两负一正,两正一负这样,可以笼统地用非之前情况来得出

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<string>
#include<math.h>
#include<iomanip>
using namespace std;
struct Vector
{
	int x, y;
	Vector(int x,int y):x(x),y(y){}
	Vector(){}
	int outer_product(Vector other)
	{
		return x * other.y - y * other.x;
	}
	int iner_product(Vector other)
	{
		return x * other.x + y * other.y;
	}
}v[3];
int px[5], py[5];
int main()
{
	ios::sync_with_stdio(false);
	int x, y;
	scanf("(%d,%d)\n", &px[0], &py[0]);
	scanf("(%d,%d)\n", &px[1], &py[1]);
	scanf("(%d,%d)\n", &px[2], &py[2]);
	scanf("(%d,%d)", &x, &y);
	v[0] = Vector(px[0] - x, py[0] - y);
	v[1] = Vector(px[1] - x, py[1] - y);
	v[2] = Vector(px[2] - x, py[2] - y);//构造了由给定点到三角形三个顶点的向量
	int p[3];//储存外积结果
	p[0] = v[0].outer_product(v[1]);
	p[1] = v[1].outer_product(v[2]);
	p[2] = v[2].outer_product(v[0]);//按顺序构造三个外积

	int negnum = 0, zeronum = 0, posnum = 0;//正,复,零的个数
	for (int i = 0; i < 3; i++)
	{
		if (p[i] > 0)posnum++;
		else if (p[i] == 0)zeronum++;
		else negnum++;
	}
	if (negnum == 3 || posnum == 3)cout << 1;//恰好三个正或者三个负:在三角形内部
	else if (zeronum == 2)cout << 4;//两个内积为0,在顶点上
	else if (zeronum == 1)//有一个外积是0,这下面有可能在边的延长线上,也可能在边上
	{
		int check = 0;
		for (int i = 0; i < 3; i++)
		{
			if (v[i].outer_product(v[(i + 1) % 3]) == 0) { check = v[i].iner_product(v[(i + 1) % 3]); break; }
			//找出外积0的那两个向量,并且求出他们的内积
		}
		if (check < 0)cout << 3;//在边上
		else cout << 2;//在外
	}
	else cout << 2;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值