1. 程式人生 > >POJ2318 Toys(計算幾何,C++, 叉積判斷線段與點的位置關係,二分法)

POJ2318 Toys(計算幾何,C++, 叉積判斷線段與點的位置關係,二分法)

目錄

題目描述:

Toy

Input

演算法實現

優化

具體程式:

題目描述:

出自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 (!toypointers.empty())cout << endl;
	}
}



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


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