首页 > ACM题库 > UVA > UVa-142-Mouse Clicks(鼠标点击)[计算几何]
2014
01-28

UVa-142-Mouse Clicks(鼠标点击)[计算几何]

Problem
问题

A typical windowing system on a computer will provide a number of icons on the screen as well as some defined regions. When the mouse button is clicked, the system has to determine where the cursor is and what is being selected. For this problem we assume that a mouse click in (or on the border of) a region selects that region, otherwise it selects the closest visible icon (or icons in the case of a tie).

一般的计算机视窗系统都会在屏幕上放置很多图标和预定义的区域。当鼠标键按下时,系统会检测鼠标点击的位置并确定点中了什么。在这个问题中,我们认为鼠标点击一个区域(点在边上也算)就选中该区域,否则就选中离的最近的可见图标(可能会有多个)。

Consider the following screen:
观察下面的屏幕:

 

i1

 

A mouse click at ‘a’ will select region A. A mouse click at ‘b’ will select icon 1. A mouse click at ‘c’ will select icons 6 and 7. A mouse click at ‘d’ is ambiguous. The ambiguity is resolved by assuming that one region is in front of another. In the data files, later regions can be assumed to be in front of earlier regions. Since regions are labelled in order of appearance (see later) ‘d’ will select C. Note that regions always overlap icons so that obscured icons need not be considered and that the origin (0,0) is at the top left corner.
鼠标点在“a”处将选中区域A,点在“b”处将选中图标1,点在“c”处将选中图标6和7,而点在’d'处则会产生一个歧意。当产生歧意时应选中较为靠前的一个区域。在输入的数据中,后给出的区域应被视为在先给出的区域之前。注意,区域将会覆盖相应位置的所有图标,被覆盖的图应被忽略。坐标原点(0, 0)位于左下角。

Write a program that will read in a series of region and icon definitions followed by a series of mouse clicks and return the selected items. Coordinates will be given as pairs of integers in the range 0..499 and you can assume that all icons and regions lie wholly within the screen. Your program must number all icons (even invisible ones) in the order of arrival starting from 1 and label regions alphabetically in the order of arrival starting from ‘A’.
写一个程序读入一组区域和图标的定义,以及一组鼠标点击的位置,并返回选中的对象。每个坐标都是两个0到499的整数,你可以认为所有的图标和区域都没有在屏幕外面的部分。你的程序应该按输入的顺序从1开始对图标进行编号,并按照输入的顺序从“A”开始以字母表对区域进行编号。

 

Input
输入

Input will consist of a series of lines. Each line will identify the type of data: I for icon, R for region and M for mouse click. There will be no separation between the specification part and the event part, however no icon or region specifications will follow the first mouse click. An I will be followed by the coordinates of the centre of the icon, R will be followed by the coordinates of the top left and bottom right corners respectively and M will be followed by the coordinates of the cursor at the time of the click. There will always be at least one visible icon and never more than 25 regions and 50 icons. The entire file will be terminated by a line consisting of a single #.
输入由多行组成。每行的开头标注了数据的类型:I为图标,R为区域,M为鼠标点击位置。数据定义部分的输入和点击事件部分的输入之间没有间隔,但开始输入鼠标数据后,将不再会出现图标或区域数据。标志I的后面是图标中心的坐标地,标志R后面是左上角和右下角的坐标,标志M后面是鼠标点击时指针位置的坐标。至少会有一个可见的图标,最多有25个区域和50个图标。全部输入的数据由独占一行的单个字符#号表示结束。

 

Output
输出

Output will consist of one line for each mouse click, containing the selection(s) for that click. Regions will be identified by their single character identifier, icon numbers will be written out right justified in a field of width 3, and where there is more than one icon number they will appear in increasing numerical order.
对应于每一个鼠标点击的事件,输出一行选中的对象。区域由单个字母表示,图标的应按右对齐,宽度为3的格式输出,对于多于一个图标被选中的情况应按照编号递增的顺序输出。

 

Sample input
输入示例

I       216     28
R       22      19      170     102
I       40      150
I       96      138
I       36      193
R       305     13      425     103
I       191     184
I       387     200
R       266     63      370     140
I       419     134
I       170     102
M       50      50
M       236     30
M       403     167
M       330     83
#

 

Sample output
输出示例

A
  1
  6  7
C

 

Analysis
分析

这道题没有什么复杂的算法,判断点在矩形内和计算距离都是最基本的运算,直接按题目做就可以了。注意两点:输入的矩形和图标不能随意改变顺序或删除,否则在输出时无法得到正确的编号;不要忘记忽略掉被矩形覆盖掉的图标。

 

Solution
解答

#include <iomanip>
#include <iostream>
#include <iterator>
#include <deque>
using namespace std;
struct POINT {int x; int y;};
struct RECT {POINT tl; POINT br;};
//判断点是否在矩形内
bool PointInRect(const POINT &pt, const RECT &rc) {
	return (pt.x >= rc.tl.x && pt.x <= rc.br.x &&
		pt.y >= rc.tl.y && pt.y <= rc.br.y);
}
//计算两点计距离的平方
int PointDistance(const POINT &pt1, const POINT &pt2) {
	POINT Vec = {pt2.x - pt1.x, pt2.y - pt1.y};
	return (Vec.x * Vec.x + Vec.y * Vec.y);
}
//主函数
int main(void) {
	deque<POINT> Icons, Clicks;
	deque<RECT> Rects;
	//循环读入每一组数据
	for (char Tag = 0; cin >> Tag && Tag != '#'; ) {
		RECT rc;
		switch (Tag) {
		case 'I': //输入图标数据
			cin >> rc.tl.x >> rc.tl.y;
			Icons.push_back(rc.tl);
			break;
		case 'R': //输入矩形数据
			cin >> rc.tl.x >> rc.tl.y >> rc.br.x >> rc.br.y;
			if (rc.br.x < rc.tl.x) *(int*)0 = 0;
			if (rc.br.y < rc.tl.y) *(int*)0 = 0;
			Rects.push_back(rc);
			if (Rects.size() > 26) *(int*)0 = 0;
			break;
		case 'M': //输入鼠标点击事件(坐标)
			cin >> rc.tl.x >> rc.tl.y;
			Clicks.push_back(rc.tl);
			break;
		}
	}
	deque<POINT>::iterator iIcon, iClick;
	deque<RECT>::iterator iRect;
	//检查是否存在图标被矩形覆盖
	for (iIcon = Icons.begin(); iIcon != Icons.end(); ++iIcon) {
		for (iRect = Rects.begin(); iRect != Rects.end(); ++iRect) {
			if (PointInRect(*iIcon, *iRect)) {
				iIcon->x = 10000; //将该图标移至无穷远处,即将其忽略
				iIcon->y = 10000; //不能删除,以免影响后面图标的编号
			}
		}
	}
	//处理每一次鼠标点击事件
	for (iClick = Clicks.begin(); iClick != Clicks.end(); ++iClick) {
		//按题目要求,从后向前检查是否点在某个矩形内
		deque<RECT>::reverse_iterator riRect;
		for (riRect = Rects.rbegin(); riRect != Rects.rend() &&
			!PointInRect(*iClick, *riRect); ++riRect);
		//如果找到点在某个矩型,输出该矩形
		if (riRect != Rects.rend()) {
			cout << (char)(distance(riRect, Rects.rend()) - 1 + 'A') << endl;
			continue;
		}
		//查找离鼠标位置距离最近的图标,存到动态数组SelIcons中
		deque<int> SelIcons(1, 1);
		deque<POINT>::iterator iNear = Icons.begin(), iIcon;
		int nNear = PointDistance(*iClick, *iNear);
		for (iIcon = iNear + 1; iIcon != Icons.end(); ++iIcon) {
			int nDist = PointDistance(*iClick, *iIcon); //计算距离
			if (nDist < nNear) {
				iNear = iIcon;
				nNear = nDist;
				SelIcons.clear(); //清空原先的数组,以新图标代替
				SelIcons.push_back(distance(Icons.begin(), iIcon) + 1);
			}
			else if (nDist == nNear) { //发现同为最短距离的图标则加入数组
				SelIcons.push_back(distance(Icons.begin(), iIcon) + 1);
			}
		}
		//按要求的格式输出图标的编号
		deque<int>::iterator iSel;
		for (iSel = SelIcons.begin(); iSel != SelIcons.end(); ++iSel) {
			cout << setw(3) << *iSel;
		}
		cout << endl;
	}
	return 0;
}