题意:给定平面上的封闭多边形和一个观测点,求这个图形是不是简单多边形(边不相交),如果是简单多边形,求从观测点能够看到的所有边。
解题思路:
- 确定是不是简单多边形非常简单,检验每条边与其他的边有没有交点即可
- 第二个问题略复杂,首先以观测点为原点O,得到多边形每个顶点A的坐标。然后根据这个坐标得到x轴正方向逆时针旋转到与向量OA重合所转过的弧度angle,这个angle的范围为[0, 2pi),将这些点按照弧度升续排列得到集合S
- 每次取S中相邻两项i和i+1(S最后一项与第一项相邻),将观测者的视角限定在由这两个点圈定的弧度范围内。求这两个点A, B的中点C,那么与射线OC相交的第一个点所在的边就是能够被看到的。求交点可以用USACO中阅读材料(http://cerberus.delos.com:791/usacotext2?a=cY7GKShmD1w&S=geom)中的“Two line segments intersect”来完成,其中求i, j可以用克拉默法则。求出i, j后,必须要i > 0且0<=j<=1,射线和边才有交点。而与射线OC相交的第一个点对应的i是最小的
- 通过3就能得到每一种视角下可看到的边,将其整合起来就能得到最后的答案,输出格式问题就不谈了
代码:
/*
ID: zc.rene1
LANG: C
PROG: fence4
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define MAX 99999999
int N;
int observer[2];
int corners[200][2];
int corners_in_observer[200][2];
double angle[200];
double pi;
int angle_order[200];
int angle_order_reverse[200];
int angle_color[200];
int ab[2], ac[2], ad[2], ca[2], cb[2], cd[2];
int visible[200];
int total_visible = 0;
int GetDistance(int a[2], int b[2])
{
int x, y;
x = a[0] - b[0];
y = a[1] - b[1];
return x * x + y * y;
}
int GetCrossProduct(int a[2], int b[2])
{
return a[0] * b[1] - a[1] * b[0];
}
int IsCross(int a[2], int b[2], int c[2], int d[2])
{
int m;
for (m=0; m<2; m++)
{
ab[m] = b[m] - a[m];
ac[m] = c[m] - a[m];
ad[m] = d[m] - a[m];
ca[m] = a[m] - c[m];
cb[m] = b[m] - c[m];
cd[m] = d[m] - c[m];
}
if (GetCrossProduct(ab, ac) * GetCrossProduct(ab, ad) >= 0)
{
return 0;
}
if (GetCrossProduct(cd, ca) * GetCrossProduct(cd, cb) >= 0)
{
return 0;
}
return 1;
}
int IsValid(void)
{
int i, j;
for (i=0; i<N; i++)
{
for (j=0; j<N; j++)
{
if (i != j)
{
if (IsCross(corners[i], corners[(i + 1) % N], corners[j], corners[(j + 1) % N]))
{
return 0;
}
}
}
}
return 1;
}
/*return the angle of vector(a, b) from X-axis, 0~2pi*/
double NewAtan2(int a, int b)
{
double temp = atan2(b, a);
if (temp < 0)
{
temp += (pi + pi);
}
return temp;
}
void SortAngle(void)
{
double min;
int i, min_i;
int has_process = 0;
memset(angle_order, -1, N * sizeof(int));
while (has_process < N)
{
min = 10.0;
min_i = -1;
for (i=0; i<N; i++)
{
if (angle_order[i] == -1)
{
if (angle[i] < min)
{
min = angle[i];
min_i = i;
}
}
}
angle_order[min_i] = has_process;
angle_order_reverse[has_process] = min_i;
has_process++;
}
}
void GetColors(void)
{
int i, j, index1, index2;
int color;
int ax, ay, bx, by, cx, cy, dx, dy;
int a, b, c, d, e, f;
int D, Dm, Dn;
double m, n, min_m;
int visible_index;
memset(visible, 0, 200*sizeof(int));
ax = 0;
ay = 0;
for (i=0; i<N; i++)
{
index1 = angle_order_reverse[i];
index2 = angle_order_reverse[(i + 1) % N];
if (angle[index1] == angle[index2])
{
continue;
}
bx = corners_in_observer[index1][0] + corners_in_observer[index2][0];
by = corners_in_observer[index1][1] + corners_in_observer[index2][1];
min_m = MAX;
visible_index = -1;
for (j=0; j<N; j++)
{
cx = corners_in_observer[j][0];
cy = corners_in_observer[j][1];
dx = corners_in_observer[(j+1)%N][0];
dy = corners_in_observer[(j+1)%N][1];
a = bx - ax;
b = cx - dx;
c = cx - ax;
d = by - ay;
e = cy - dy;
f = cy - ay;
D = a * e - b * d;
Dm = c * e - b * f;
Dn = a * f - c * d;
if (D == 0)
{
continue;
}
else
{
m = Dm * 1.0 / D;
n = Dn * 1.0 / D;
if ((m > 0) && (n >= 0) && (n <= 1))
{
if (m < min_m)
{
min_m = m;
visible_index = j;
}
}
}
}
if (visible_index == -1)
{
continue;
}
if (visible[visible_index] == 0)
{
visible[visible_index] = 1;
total_visible++;
}
}
}
int main(void)
{
FILE *fin, *fout;
int i;
fin = fopen("fence4.in", "r");
fout = fopen("fence4.out", "w");
/*get the input*/
fscanf(fin, "%d", &N);
fscanf(fin, "%d %d", &observer[0], &observer[1]);
pi = atan2(0, -1);
for (i=0; i<N; i++)
{
fscanf(fin, "%d %d", &corners[i][0], &corners[i][1]);
corners_in_observer[i][0] = corners[i][0] - observer[0];
corners_in_observer[i][1] = corners[i][1] - observer[1];
angle[i] = NewAtan2(corners[i][0] - observer[0], corners[i][1] - observer[1]);
}
/*check if valid*/
if (!IsValid())
{
fprintf(fout, "NOFENCE\n");
return 0;
}
/*sort the angle, generate the order of angle*/
SortAngle();
/*core algorithm*/
GetColors();
/*output the result*/
fprintf(fout, "%d\n", total_visible);
for (i=0; i<(N-2); i++)
{
if (visible[i] == 1)
{
fprintf(fout, "%d %d %d %d\n", corners[i][0], corners[i][1], corners[i+1][0], corners[i+1][1]);
}
}
if (visible[N-1] == 1)
{
fprintf(fout, "%d %d %d %d\n", corners[0][0], corners[0][1], corners[N-1][0], corners[N-1][1]);
}
if (visible[N-2] == 1)
{
fprintf(fout, "%d %d %d %d\n", corners[N-2][0], corners[N-2][1], corners[N-1][0], corners[N-1][1]);
}
return 0;
}