题目概述
一个房间,左下角坐标(0,0),右上角坐标(10,10),A把某物藏于某处,B从(0,0)出发,每次走到一个位置,A会说距离藏物点更近了(Hotter),更远了(Colder)或距离没变(Same),给定每次B到的位置(x,y)和A喊的话,求可能藏物的区域的面积
A每次只会说Hotter,Colder或Same
时限
1000ms/3000ms
输入
每行两个浮点数x,y,代表B到的位置,以及一个字符串,代表A说的话,输入到EOF为止
限制
输入最多包含50行
输出
每行一个保留两位小数的浮点数,为所求可行域的面积
样例输入
10.0 10.0 Colder
10.0 0.0 Hotter
0.0 0.0 Colder
10.0 10.0 Hotter
样例输出
50.00
37.50
12.50
0.00
讨论
计算几何,半平面交,不知是题水还是额变得谨慎了,竟然一发拿下,有点受宠若惊
首先要明白题意,每次移动后,现在的和上次的两个点连成一条线段,其中垂线构成半平面,因为中垂线上的点和移动前后的位置距离都相等,由喊的话决定可行域的方向,初始的可行域是整个房间,每次移动,进行一次切割,并求出其面积,就是这样
接下来是实现部分,这次表示半平面的方式比上一次poj 1755还要麻烦一点,没有线上的点,没有任何参数,借用向量式的思想(记得蓝白数上用的就是向量式),找到半平面上一个点及其方向向量,点用中点就行,求方向向量时,先以坐标表示出移动前后两点的方向向量,(x2-x1,y2-y1),然后交换逗号左右二者,并将其中一个取相反数,得到的一种形式是(y2-y1,x1-x2),然后将其与中点做和,得到半平面上第二个点,两点式,额很熟悉,接下来确定喊话的意义,其实画个图就能看出来,在上面得到的形式下,Hotter是左侧,Colder是右侧,Same比较特殊,另谈,确定左右后就可以根据每次的位置,喊话内容计算半平面交了,当出现Same或后面喊的话与前面矛盾的时候,什么也不用做,之后可行域会被更新为空,往后面积就全是0了,求面积时需要注意一点,虽然对任何简单多边形利用向量积都能算出面积,不过那是有向面积,由于最后点集里点的顺序不确定,因而输出时需要带绝对值
精度沿用的上一题(poj 1755)的,忘了改了
题解状态
196K,0MS,C++,2115B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 300
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-16
struct Pt//点的结构
{
double x, y;
Pt() {}
Pt(double x, double y) :x(x), y(y) {}//方便初始化前四个点写的构造函数
}pt1[MAXN] = { Pt(0,0),Pt(10,0),Pt(10,10),Pt(0,10) }, pt2[MAXN];//两个半平面交辅助数组(点集) 事先以房间边界初始化
int hpt1, hpt2;//两个点集中点的数量
int signal(double a)//符号函数 EPS小的话应该会有不少漏网之鱼 可见数据之弱
{
if (abs(a) < EPS)
return 0;
return a > 0 ? 1 : -1;
}
double xp(Pt &a, Pt &b, Pt &c)//熟悉的向量积 现在普遍青睐三个点的形式而不是六个double
{
return (a.x - b.x)*(c.y - b.y) - (a.y - b.y)*(c.x - b.x);
}
Pt point_of_intersection(Pt &a, Pt &b, Pt &c, Pt &d)//求交点 无论是两点一式 两线 四点 八double 核心部分都是一样的
{
double Sabc = xp(c, a, b), Sabd = xp(d, a, b);
return Pt((Sabc*d.x - Sabd*c.x) / (Sabc - Sabd), (Sabc*d.y - Sabd*c.y) / (Sabc - Sabd));//仍然是定比分点公式
}
void fun()
{
double x1 = 0, y1 = 0, x2, y2;//上一次的位置 这一次的位置
char str[10];//存放喊话的字符串
hpt1 = 4;//一开始点集1有4个点 房间边界
while (~scanf("%lf%lf%s", &x2, &y2, str)) {//input
Pt p1((x1 + x2) / 2, (y1 + y2) / 2), p2(y2 - y1 + (x1 + x2) / 2, x1 - x2 + (y1 + y2) / 2);//根据前后两点揪出半平面上的两个点 表示半平面
hpt2 = 0;//点集2初始为0
for (int p = 0; p < hpt1; p++) {//下面分情况进行半平面交
if (str[0] == 'H') {//热了取左侧
if (signal(xp(pt1[p], p1, p2)) <= 0)
pt2[hpt2++] = pt1[p];
else {
if (signal(xp(pt1[(p - 1 + hpt1) % hpt1], p1, p2)) < 0)
pt2[hpt2++] = point_of_intersection(pt1[p], pt1[(p - 1 + hpt1) % hpt1], p1, p2);
if (signal(xp(pt1[(p + 1) % hpt1], p1, p2)) < 0)
pt2[hpt2++] = point_of_intersection(pt1[p], pt1[(p + 1) % hpt1], p1, p2);
}
}
else if (str[0] == 'C') {//冷了取右侧
if (signal(xp(pt1[p], p1, p2)) >= 0)
pt2[hpt2++] = pt1[p];
else {
if (signal(xp(pt1[(p - 1 + hpt1) % hpt1], p1, p2)) > 0)
pt2[hpt2++] = point_of_intersection(pt1[p], pt1[(p - 1 + hpt1) % hpt1], p1, p2);
if (signal(xp(pt1[(p + 1) % hpt1], p1, p2)) > 0)
pt2[hpt2++] = point_of_intersection(pt1[p], pt1[(p + 1) % hpt1], p1, p2);
}
}
}//Same的情况什么也不用做 由于点集2没有点 自然会返回0面积
for (int p = 0; p < hpt2; p++)
pt1[p] = pt2[p];
hpt1 = hpt2;
double area = 0;
for (int p = 0; p < hpt1 - 1; p++)
area += xp(pt1[p], pt1[0], pt1[(p + 1) % hpt1]);
printf("%.2lf\n", abs(area / 2));//output//注意面积需要取绝对值
x1 = x2, y1 = y2;//更新移动点
}
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
fun();//有史以来最短的main函数 其中两行还是注释
}