![]()
![]()
直角多边形:给定N个点,问是否能组成直角多边形(每个顶点都与另外两个顶点构成直角,每条边都平行于坐标轴),并求出周长?
3.6与平面和空间打交道的计算几何
平面扫描
扫描线移动时,如果线上的点数为偶数,则相邻两个点构成一条边,记录下来;如果为奇数,则无法构成(余下一个点无法构成边)。切换扫描线到另一坐标轴,执行相似的逻辑,不过还需检查横向的边是否与纵向的边相交,如果相交,则无法构成。
#include <iostream>
#include <algorithm>
using namespace std;
#define MAX_N 100000 + 16
int link_x[MAX_N], // 水平方向相连的顶点
link_y[MAX_N]; // 垂直方向相连的顶点
struct Point
{
int x, y;
int id;
int get(const bool& is_x)
{
return is_x ? x : y;
}
int get_link(const bool& is_x)
{
return is_x ? link_x[id] : link_y[id];
}
void link(const bool& is_x, const Point& to)
{
(is_x ? link_x[id] : link_y[id]) = to.id;
(is_x ? link_x[to.id] : link_y[to.id]) = id;
}
}ps[MAX_N];
struct Line
{
int x, y1, y2;
Line(){}
Line(int x, int y1, int y2) :x(x), y1(y1), y2(y2){}
}ls[MAX_N];
bool is_x;
int cmp(Point& a, Point& b)
{
int ax = a.get(is_x);
int bx = b.get(is_x);
int ay = a.get(!is_x);
int by = b.get(!is_x);
if (ax == bx) return ay < by;
return ax < bx;
}
bool check_intersect(const Point& a, const Point& b, const int& ln)
{
int y = a.y, x1 = a.x, x2 = b.x;
for (int i = 0; i < ln; i++)
{
if (x1 < ls[i].x && x2 > ls[i].x && ls[i].y1 < y && ls[i].y2 > y)
return true;
}
return false;
}
int M, N;
int traverse(int& ln)
{
sort(ps, ps + N, cmp);
int count_same_line = 1;
int sum = 0;
for (int i = 1; i < N; ++i)
{
if (ps[i].get(is_x) != ps[i - 1].get(is_x))
{
// 扫描线移动
if (count_same_line & 1)
{
return -1;
}
count_same_line = 1;
}
else
{
// 同一条扫描线上的点遍历
++count_same_line;
if (!(count_same_line & 1))
{
sum += ps[i].get(!is_x) - ps[i - 1].get(!is_x);
ps[i].link(is_x, ps[i - 1]);
if (is_x)
{
// 横向扫描记录竖向直线
ls[ln++] = Line(ps[i].x, ps[i - 1].y, ps[i].y);
}
else
{
// 竖向扫描检查是否相交
if (check_intersect(ps[i - 1], ps[i], ln))
{
return -1;
}
}
}
}
}
return sum;
}
int solve()
{
int ln = 0;
is_x = true;
int x = traverse(ln);
if (x == -1) return -1;
is_x = false;
int y = traverse(ln);
if (y == -1) return -1;
int cur = 0;
int link_count = 0;
do
{
cur = is_x ? link_x[cur] : link_y[cur];
is_x = !is_x;
++link_count;
} while (cur != 0);
if (link_count != N)
{
return -1;
}
return x + y;
}
///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d", &M);
while (M--)
{
scanf("%d", &N);
for (int i = 0; i < N; ++i)
{
scanf("%d%d", &ps[i].x, &ps[i].y);
ps[i].id = i;
}
printf("%d\n", solve());
}
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
system("out.txt");
#endif
return 0;
}
///////////////////////////End Sub//////////////////////////////////
| 13890173 | hankcs | 3293 | Accepted | 760K | 250MS | C++ | 2743B | 2015-02-14 23:59:14 |
如果你忘了做相交检查的话,以下用例会输出错误结果16:
1 6 0 0 1 0 0 1 2 1 1 2 2 2
知识共享署名-非商业性使用-相同方式共享:码农场 » POJ 3293 Rectilinear polygon 题解 《挑战程序设计竞赛》
码农场
其实把X方向优先排一次序,再把Y方向优先排一次序,可行解在这两个数组是中间就已经是相邻顶点了.
关于相交的问题,因为在处理Y方向的时候X方向也排序好了,所以只需要二分查找当前横线段的左右端点间的竖线段判断相交,最后一个DFS就行,时间比楼主快,47MS