码迷,mamicode.com
首页 > 其他好文 > 详细

POJ-2318 TOYS(二分)(叉乘)(判断点在直线的哪侧)

时间:2020-07-10 00:42:24      阅读:90      评论:0      收藏:0      [点我收藏+]

标签:平移   set   ons   move   inline   else   分析   ros   name   

题意:给出一个箱子的左上角和右下角的坐标,可以插入一些板子,每块板子的上顶点和下顶点。然后给定一堆玩具,扔到箱子里,求每个分隔区域里面的玩具数量。

分析:因为板子的坐标是按顺序从小到大给出的,我们的一个点,如果在一个板子的左边,那么就在这块板子后面的板子的坐标,但是在左侧板子的右边,具有单调性,可以用二分。判断一个点是否在一个直线的左右侧的时候,我们可以使用叉乘。
技术图片
如果\(\vec{pa} \times \vec{pb}\) < 0,表示\(p\)点在\(AB\)的左侧,如果>0,表示在\(AB\)的右侧,== 0则表示在\(AB\)的线上。

\(时间复杂度o(mlogn),n表示板子的数量,m表示玩具,logn表示对n块板子的位置二分。\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>

using namespace std;

const double eps = 1e-10;
const double PI = acos(-1.0);

struct Point
{
	double x, y;
	Point(double x = 0, double y = 0) :x(x), y(y) {}
	void operator=(const Point& rhs)
	{
		this->x = rhs.x;
		this->y = rhs.y;
	}
};

typedef Point Vector;

//向量 + 向量 = 向量, 点 + 向量 = 点
Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); }

//点 - 点 = 向量
Vector operator - (Point A, Point B) { return Vector(A.x - B.x, A.y - B.y); }

//向量 * 标量 = 向量
Vector operator * (Vector A, double p) { return Vector(A.x * p, A.y * p); }

//向量 / 数 = 向量
Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y / p); }

int dcmp(double x)
{
	if (fabs(x) < eps) return 0;
	else return x < 0 ? -1 : 1;
}

bool operator<(const Point& a, const Point& b)
{
	return dcmp(a.x - b.x) < 0 || dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) < 0;
}

bool operator == (const Point& a, const Point& b)
{
	return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

//求叉积
double Cross(Vector A, Vector B) { return A.x * B.y - A.y * B.x; }
double Dot(Vector A, Vector B) { return A.x * B.x + A.y * B.y; }
double Length(Vector A) { return sqrt(Dot(A, A)); }

Vector Normal(Vector A)
{
	double L = Length(A);
	return Vector(-A.y / L, A.x / L);
}

struct Line {
	Point p;
	Vector v;
	Line() {};
	Line(Point p, Vector v) :p(p), v(v) { }
	//直线上的点坐标
	Point point(double t) {
		return p + v * t;
	}
	//平移直线d距离
	Line move(double d) {
		return Line(p + Normal(v) * d, v);
	}
};

const int N = 5050;
Line lines[N];

int cnt[N];

bool check(Point q, int mid)
{
	//直线两端的点
	Point a = lines[mid].point(1);
	Point b = lines[mid].point(0);

	Vector qa(a - q);
	Vector qb(b - q);

	if (dcmp(Cross(qa, qb)) < 0) return true;
	return false;
}

void init()
{
	memset(lines, 0, sizeof lines);
	memset(cnt, 0, sizeof cnt);
}

int main()
{
	int n, m;
	double x1, y1, x2, y2;

	while (scanf("%d", &n) != EOF)
	{
		if (n == 0) break;
		scanf("%d%lf%lf%lf%lf", &m, &x1, &y1, &x2, &y2);

		lines[0].p = Point(0, y2);
		lines[0].v = Point(x1, y1 - y2);
		//每个隔板的直线
		double t, b;
		for (int i = 1; i <= n; ++i)
		{
			scanf("%lf%lf", &t, &b);
			lines[i].p = Point(b, y2);
			lines[i].v = Point(t - b, y1 - y2);
		}

		lines[n + 1].p = Point(x2, y2);
		lines[n + 1].v = Point(0, y1 - y2);

		double toyx, toyy;
		//m个玩具
		for (int i = 1; i <= m; ++i)
		{
			scanf("%lf%lf", &toyx, &toyy);
			Point toy(toyx, toyy);
			//二分在哪个板子左侧
			int l = 0, r = n + 1;
			while (l < r)
			{
				int mid = l + r >> 1;
				if (check(toy, mid)) r = mid;
				else l = mid + 1;
			}
			++cnt[l - 1];
		}

		for (int i = 0; i <= n; ++i)
		{
			printf("%d: %d\n", i, cnt[i]);
		}
		puts("");
		init();
	}
	return 0;
}

POJ-2318 TOYS(二分)(叉乘)(判断点在直线的哪侧)

标签:平移   set   ons   move   inline   else   分析   ros   name   

原文地址:https://www.cnblogs.com/pixel-Teee/p/13276889.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!