UVa Problem 10084 Hotter Colder (冷热游戏)

来源:互联网 发布:api接口 聚合数据 编辑:程序博客网 时间:2024/04/28 08:25
// Hotter Colder (冷热游戏)// PC/UVa IDs: 111404/10084, Popularity: C, Success rate: low Level: 3// Verdict: Accepted// Submission Date: 2011-11-09// UVa Run Time: 0.008s//// 版权所有(C)2011,邱秋。metaphysis # yeah dot net//// [解题方法]// 本题的实质是半平面与凸多边形求交问题,表示半平面的直线即相邻两个位置连线的垂直平分线。由于初始// 区域为凸多边形,每次切割后仍为凸多边形。#include <iostream>#include <iomanip>#include <cmath>#include <queue>using namespace std;#define MAXPOLY 60#define EPSILON (1E-7)#define SAME_LINE 0#define PARALLE_LINE 1#define LINE_NO_INTERSECT 2#define LINE_INTERSECT 3#define LIE_ON_LINE 0#define BELOW_LINE (-1)#define ABOVE_LINE 1struct point{double x;double y;};struct polygon{int n;point p[MAXPOLY];};struct line{double a, b, c;};struct segment{point p1, p2;};polygon region;double lastX = 0.0, lastY = 0.0, lastArea = 100.0;bool isLineOrEmpty;// 判断两条直线是否平行。bool paralleLine(line l1, line l2){return ((fabs(l1.a - l2.a) <= EPSILON) && (fabs(l1.b - l2.b) <= EPSILON));}// 判断两条直线是否重合。bool sameLine(line l1, line l2){return (paralleLine(l1, l2) && (fabs(l1.c - l2.c) <= EPSILON));}// 点是否在线段的包围盒内。bool point_in_box(point p, point b1, point b2){return ((p.x >= min(b1.x, b2.x)) && (p.x <= max(b1.x, b2.x))&& (p.y >= min(b1.y, b2.y)) && (p.y <= max(b1.y, b2.y)));}// 求两直线的交点。int intersection_point(line l1, line l2, segment s, point &p){if (sameLine(l1, l2))return SAME_LINE;if (paralleLine(l1, l2))return PARALLE_LINE;p.x = (l2.b * l1.c - l1.b * l2.c) / (l2.a * l1.b - l1.a * l2.b);if (fabs(l1.b) > EPSILON)p.y = -(l1.a * p.x + l1.c) / l1.b;elsep.y = -(l2.a * p.x + l2.c) / l2.b;if (point_in_box(p, s.p1, s.p2))return LINE_INTERSECT;elsereturn LINE_NO_INTERSECT;}// 通过两点求直线方程。void points_to_line(point p1, point p2, line &l){if (p1.x == p2.x){l.a = 1;l.b = 0;l.c = -p1.x;}else{l.b = 1;l.a = -(p1.y - p2.y) / (p1.x - p2.x);l.c = -(l.a * p1.x) - (l.b * p1.y);}}// 使用斜率和一点求直线方程。void point_and_slope_to_line(point p, double m, line &l){l.a = -m;l.b = 1;l.c = -((l.a * p.x) + (l.b * p.y));}// 利用有向面积计算多边形的面积,注意最后结果取绝对值,因为顶点顺序可能并不是按逆时针方向给出。double calArea(point vertex[], int vertexNumber){double total = 0.0;for (int i = 0; i < vertexNumber; i++){int j = (i + 1) % vertexNumber;total += (vertex[i].x * vertex[j].y - vertex[j].x * vertex[i].y);}return fabs(total / 2.0);}// 判断点和直线的关系。int relationPointLine(line l, point p){if (fabs(l.a * p.x + l.b * p.y + l.c) <= EPSILON)return LIE_ON_LINE;if ((l.a * p.x + l.b * p.y + l.c) > 0)return ABOVE_LINE;elsereturn BELOW_LINE;}// 求半平面和凸多边形交的面积。double area(double x, double y, string status){// 若坐标未变化,则返回最后计算所得到的面积。if (x == lastX && y == lastY)return lastArea;// 当可能区域为一条线段或者不可能存在这样的区域时,返回 0 值。if (isLineOrEmpty)return 0.0;// 若给出的是 Same,且前后位置坐标不同,则物品在线段的垂直平分线上。if (status == "Same"){isLineOrEmpty = true;return 0.0;}// 求端点为(x,y),(lastX,lastY)的线段的垂直平分线直线方程。line l1, l2;point middle;middle.x = (lastX + x) / 2.0;middle.y = (lastY + y) / 2.0;if (lastY == y){l1.a = 1;l1.b = 0;l1.c = -middle.x;}else{double slope = -(lastX - x) / (lastY - y);point_and_slope_to_line(middle, slope, l1);}// 判断是那个半平面与凸多边形相交。可以通过起点和垂直平分线的关系以及提示语来判断。若// upPlane 为真,表示 a * x + b * y + c > 0 的半平面与凸多边形相交,反之则是另外// 一个半平面与之相交。bool upPlane = ((l1.a * x + l1.b * y + l1.c) > 0);if (status == "Colder")upPlane = !upPlane;// 判断直线与多边形的相交情况,总是顺时针枚举凸包的顶点,以便生成的凸多边形顶点顺序仍然// 是顺时针方向。queue < point > up, down;for (int i = 0; i < region.n; i++){int j = (i + 1) % region.n;// 直线和点的位置关系。int ri = relationPointLine(l1, region.p[i]);int rj = relationPointLine(l1, region.p[j]);// 凸多边形两个相邻顶点在直线的异侧,表明直线和该线段有交点,求交点。if (ri * rj < 0){points_to_line(region.p[i], region.p[j], l2);point intersect;segment s = (segment) { region.p[i], region.p[j] };intersection_point(l1, l2, s, intersect);if (ri < 0){down.push(region.p[i]);down.push(intersect);up.push(intersect);}else{up.push(region.p[i]);up.push(intersect);down.push(intersect);}}// 凸多边形两个顶点或单个顶点在直线上。else if (ri * rj == 0){if (ri == 0 && rj == 0){up.push(region.p[i]);down.push(region.p[i]);}else if (ri == 0 && rj < 0){up.push(region.p[i]);down.push(region.p[i]);}else if (ri == 0 && rj > 0){down.push(region.p[i]);up.push(region.p[i]);}else if (ri < 0 && rj == 0)down.push(region.p[i]);else if (ri > 0 && rj == 0)up.push(region.p[i]);}// 凸多边形的两个顶点在直线的同侧。else{if (ri > 0)up.push(region.p[i]);elsedown.push(region.p[i]);}}// 根据所在半平面取相应的顶点。if (upPlane){region.n = 0;while (!up.empty()){region.p[(region.n)++] = up.front();up.pop();}}else{region.n = 0;while (!down.empty()){region.p[(region.n)++] = down.front();down.pop();}}// 计算面积。return calArea(region.p, region.n);}int main (int argc, char const* argv[]){double x, y;string status;cout.precision(2);cout.setf(ios::fixed | ios::showpoint);// 初始凸多边形即为给定的正方形范围顶点。region.n = 4;region.p[0] = (point) { 0.0, 0.0 };region.p[1] = (point) { 0.0, 10.0 };region.p[2] = (point) { 10.0, 10.0 };region.p[3] = (point) { 10.0, 0.0 };lastX = lastY = 0.0;isLineOrEmpty = false;// 读入位置和结果。while (cin >> x >> y >> status){cout << area(x, y, status) << endl;lastX = x;lastY = y;}return 0;}


原创粉丝点击