POJ2318 Toys(计算几何,C++, 叉积判断线段与点的位置关系,二分法)

目录

题目描述:

算法实现

判断点(toys)和线段(partitions)的位置关系:

优化

具体程序:


题目描述:

出自ACM

Toy

Description

Calculate the number of toys that land in each bin of a partitioned toy box.
Mom and dad have a problem - their child John never puts his toys away when he is finished playing with them. They gave John a rectangular box to put his toys in, but John is rebellious and obeys his parents by simply throwing his toys into the box. All the toys get mixed up, and it is impossible for John to find his favorite toys.

John’s parents came up with the following idea. They put cardboard partitions into the box. Even if John keeps throwing his toys into the box, at least toys that get thrown into different bins stay separated. The following diagram shows a top view of an example toy box.

Input

The input file contains one or more problems. The first line of a problem consists of six integers, n m x1 y1 x2 y2. The number of cardboard partitions is n (0 < n <= 5000) and the number of toys is m (0 < m <= 5000). The coordinates of the upper-left corner and the lower-right corner of the box are (x1,y1) and (x2,y2), respectively. The following n lines contain two integers per line, Ui Li, indicating that the ends of the i-th cardboard partition is at the coordinates (Ui,y1) and (Li,y2). You may assume that the cardboard partitions do not intersect each other and that they are specified in sorted order from left to right. The next m lines contain two integers per line, Xj Yj specifying where the j-th toy has landed in the box. The order of the toy locations is random. You may assume that no toy will land exactly on a cardboard partition or outside the boundary of the box. The input is terminated by a line consisting of a single 0.

Output

The output for each problem will be one line for each separate bin in the toy box. For each bin, print its bin number, followed by a colon and one space, followed by the number of toys thrown into that bin. Bins are numbered from 0 (the leftmost bin) to n (the rightmost bin). Separate the output of different problems by a single blank line.

Sample Input

5 6 0 10 60 0
3 1
4 3
6 8
10 10
15 30
1 5
2 1
2 8
5 5
40 10
7 9
4 10 0 10 100 0
20 20
40 40
60 60
80 80
5 10
15 10
25 10
35 10
45 10
55 10
65 10
75 10
85 10
95 10
0

Sample Output

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

0: 2
1: 2
2: 2
3: 2
4: 2

Hint

As the example illustrates, toys that fall on the boundary of the box are “in” the box.

算法实现

题目总结起来就是给定多个分区和点的信息,求每个分区中点的数量,这是一道计算几何的基础题目,其核心的数学问题在于:

判断点(toys)和线段(partitions)的位置关系:

       

 判断一个点和一条线段的位置关系,知道线段上两端点的坐标和待判断点的坐标:线段两点S,E,待判断点P; 如果SP在SE的逆时针方向,或者说S,E,P成逆时针排列,则称P在线段SE的左侧。

       具体判断方法,将向量SE和SP表示成三维形式,其叉积的值如果为正(方向垂直坐标平面向外),则说明SP在SE的逆时针方向。

最终我们只需要判断 的值是否大于0即可

优化

具体到这个问题有两个地方可以优化。

一个是如果点(toys)的x坐标小于线段两端的x那么,该点必然在线段的左侧;反之,如果点的坐标大于线段两端的x,那么该点必然在线段的右侧。加之利用C++逻辑判断的短路原理,可将判断点是否在线段左侧的指令写为:

p.x < l.xmin || (p.x < l.xmax && (l.en.x - l.st.x)*(p.y - l.st.y) - (p.x - l.st.x)*( l.en.y - l.st.y) < 0);

二是使用二分法

具体程序:

//POJ2318_Toys.h,POJ2318_Toys.cpp和主程序如下

//POJ2318_Toys.h


#include <iostream>
#include <math.h>
#include <algorithm>
#include <vector>

using namespace std;
class pointer{
public:
	int x;
	int y;
	pointer() : x(0), y(0) {}
	pointer(int a, int b) : x(a),  y(b)  {}
	pointer& operator =(const pointer& p) {
		x = p.x;
		y = p.y;
		return *this;
	}
};
class line {
public:
	pointer st;
	pointer en;
	int xmin;
	int xmax;
	line() {
		st = pointer();
		en = pointer();
		xmin = 0;
		xmax = 0;
	}
	line(pointer start, pointer end) : st(start), en(end), xmin(std::min(start.x, end.x)), xmax(std::max(start.x, end.x)) {}
};
class POJ2318_Toys
{
public:
	POJ2318_Toys();
	~POJ2318_Toys();
	int n, m;
	vector<pointer> LU, RD;
	//对于这种在类里面的头文件中定义的vector,若想声明vector的大小,不能在头文件中声明,可以现在头文件中声明,然后去构造函数中用resize()指明情况,
	vector<vector<line>>  porlines;
	vector<vector<pointer>> toypointers;
	bool in();
	bool left(line l,pointer p);
	int judge(pointer p, vector<line> porlines);
	void doing();
};

//POJ2318_Toys.cpp


#include "stdafx.h"
#include "POJ2318_Toys.h"
#include <unordered_map>


POJ2318_Toys::POJ2318_Toys()
{

}


POJ2318_Toys::~POJ2318_Toys()
{
}

bool POJ2318_Toys::in()
{
	cin >> n;
	if (n == 0)return false;
	pointer L, R;
	cin >> m >> L.x >> L.y >> R.x >> R.y;
	LU.push_back(L);
	RD.push_back(R);
	vector<pointer> toys;
	vector<line> pors;
//	porlines.clear();
	//toypointers.clear();
	for (int i = 0; i < n; i++) {
		pointer start, end;
		cin >> start.x >> end.x;
		start.y = L.y;
		end.y = R.y;
		line l = line(start, end);
		pors.push_back(l);
	}
	for (int j = 0; j < m; j++) {
		pointer p;
		cin >> p.x >> p.y;
		toys.push_back(p);
	}
	porlines.push_back(pors);
	toypointers.push_back(toys);
	return true;
}

//返回的是toy是否在l的左侧
bool POJ2318_Toys::left(line l, pointer p)
{
	//利用C++的短路效应
	return p.x < l.xmin || (p.x < l.xmax && (l.en.x - l.st.x)*(p.y - l.st.y) - (p.x - l.st.x)*( l.en.y - l.st.y) < 0);
}

//返回的是toy所在的分区号
int POJ2318_Toys::judge(pointer p, vector<line> porlines)
{
//	int res = 0;
//	while (res < porlines.size() && !left(porlines[res], p))res++;
	if (!left(porlines.back(), p)) return porlines.size();
	int l = 0, r = porlines.size() - 1;
	while (l <= r) {
		int suanz = (l + r) / 2;
		if (!left(porlines[suanz], p))l = suanz + 1;
		else r = suanz - 1;
	}
	return l;
}

void POJ2318_Toys::doing()
{
	cout << "the result is:" << endl;
	for (int i = 0; i < toypointers.size(); i ++) {
		vector<pointer> toys = toypointers[i];
		vector<line> pors = porlines[i];
		unordered_map<int, int>m;
		for (int i = 0; i < toys.size(); i++) {
			m[judge(toys[i], pors)] ++;
		}
		for (int i = 0; i <= pors.size(); i++)
			cout << i << ": " << m[i] << endl;
		if (i != toypointers.size() - 1)cout << endl;
	}
}



#include "stdafx.h"
#include "POJ2318_Toys.h"


int main()
{
	POJ2318_Toys PT;
	while (PT.in()) {}
	PT.doing();
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值