几何题看着就很有畏惧感。这里用的是最naive的算法,时间复杂度应该在n^2。还没看别人的解题报告,不过我猜nlogn的解法是有的。
比如判断一个fence是不是valid的时候,这里将所有的线段两两比较,看是否相交。但是有个叫line sweep的算法,可以在nlogn的时间复杂度内完成。既然accept了,就懒得实现了。。。
判断两条线段(line segment)是否相交,stackoverflow上面有很详细的讨论,各种思路都有:http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
我采用的是这里的解法:http://compgeom.cs.uiuc.edu/~jeffe/teaching/373/notes/x06-sweepline.pdf
大意是如果线段ab和cd相交,则点a和点b在线段cd的两边,且点c和点d在线段ab的两边。
前者看ac和cd以及bc和cd的向量叉乘符号是否一致。
上面链接上有详述,包括line sweep算法。
注意判断一条线端能否被观察者看到时,要排除到观察者在这条线延长线的情况。点(x,y)是否在线段(x1, y1)->(x2, y2)上面只需要判断:
(x1 - x2) * y - (y1 - y2) * x = x1 * y2 - x2 * y1
然后具体判断的时候,我用的是将整条线分成1000份,看观察者到这个等分点的连线是否与别的线相交了。
如果有一个点能看到,则这个线段能看到。
usaco标准解法里面取了中点,并且将第一条相交的线段标记为可见。这个想法很好,可以减少重复判断。
也可以先取中点,再二分,效率应该能更高。
我这种最简单。在这种小数据n=200的情况下也可以过所有测试点。
/*
ID: thestor1
LANG: C++
TASK: fence4
*/
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <climits>
#include <cassert>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 200;
int X[MAXN], Y[MAXN];
int N;
// check if ac and cd is counter-clockwise
bool isCCW(double x1, double y1, double x2, double y2, double x3, double y3)
{
// ac = (x2 - x2, y2 - y1)
// cd = (x3 - x2, y3 - y2)
return (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2) > 0;
}
bool isIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
if(isCCW(x1, y1, x3, y3, x4, y4) == isCCW(x2, y2, x3, y3, x4, y4))
{
return false;
}
else if(isCCW(x3, y3, x1, y1, x2, y2) == isCCW(x4, y4, x1, y1, x2, y2))
{
return false;
}
return true;
}
bool isValid()
{
for(int i = 3; i < N; ++i)
{
for(int j = 1; j <= i - 2; ++j)
{
if(isIntersect(X[i - 1], Y[i - 1], X[i], Y[i], X[j - 1], Y[j - 1], X[j], Y[j]))
{
return false;
}
}
}
return true;
}
bool isColine(int lineno, int x, int y)
{
int x1 = X[lineno];
int y1 = Y[lineno];
int x2 = X[(lineno + 1) % N];
int y2 = Y[(lineno + 1) % N];
return (x1 - x2) * y - (y1 - y2) * x == x1 * y2 - x2 * y1;
}
bool isSeen(int lineno, int x, int y)
{
int x1 = X[lineno];
int y1 = Y[lineno];
int x2 = X[(lineno + 1) % N];
int y2 = Y[(lineno + 1) % N];
int nsegments = 1000;
double px, py;
for(int i = 1; i < nsegments; ++i)
{
px = x1 + i * 1.0 / nsegments * (x2 - x1);
py = y1 + i * 1.0 / nsegments * (y2 - y1);
bool blocked = false;
for(int j = 0; j < N; ++j)
{
if(j == lineno)
{
continue;
}
int x2 = X[j];
int y2 = Y[j];
int x3 = X[(j + 1) % N];
int y3 = Y[(j + 1) % N];
if(isIntersect(x, y, px, py, x2, y2, x3, y3))
{
blocked = true;
break;
}
}
if(!blocked)
{
return true;
}
}
return false;
}
int main()
{
FILE *fin = fopen ("fence4.in", "r");
FILE *fout = fopen ("fence4.out", "w");
// cin>> N;
fscanf(fin, "%d", &N);
// cout<<"N: "<<N<<endl;
int x, y;
// cin>>x>>y;
fscanf(fin, "%d %d", &x, &y);
for(int i = 0; i < N; ++i)
{
fscanf(fin, "%d %d", &X[i], &Y[i]);
// cin>>X[i]>>Y[i];
// cout<<i<<": "<<X[i]<<", "<<Y[i]<<endl;
}
if(!isValid())
{
fprintf(fout, "NOFENCE\n");
// cout<<"NOFENCE"<<endl;
return 0;
}
std::vector<int> v;
for(int i = 0; i < N; ++i)
{
if(!isColine(i, x, y) && isSeen(i, x, y))
{
v.push_back(i);
}
}
if(v.size() >= 2 && v[v.size() - 2] == N - 2)
{
v[v.size() - 2] = N - 1;
v[v.size() - 1] = N - 2;
}
fprintf(fout, "%d\n", v.size());
// cout<<v.size()<<endl;
for(int i = 0; i < v.size(); ++i)
{
if(v[i] == N - 1)
{
fprintf(fout, "%d %d %d %d\n", X[0], Y[0], X[v[i]], Y[v[i]]);
// cout<<X[0]<<" "<<Y[0]<<" "<<X[v[i]]<<" "<<Y[v[i]]<<endl;
}
else
{
fprintf(fout, "%d %d %d %d\n", X[v[i]], Y[v[i]], X[v[i] + 1], Y[v[i] + 1]);
// cout<<X[v[i]]<<" "<<Y[v[i]]<<" "<<X[v[i] + 1]<<" "<<Y[v[i] + 1]<<endl;
}
}
return 0;
}