这道题花了有点时间,不擅长计算几何,搞了略久,最后0.929s卡过,写的略萎,边晒代码,边讲思路。
/*
ID: volz.kz.g
PROB: fence4
LANG: C++
*/
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
ifstream fin("fence4.in");
ofstream fout("fence4.out");
struct point_node{
double x,y;
}point[211];
struct edge_node{
int point1,point2;
}edge[211];
edge_node edge_watch[211];
point_node viewer;
int point_num;
int edge_num;
int edge_watch_num;
/*
*快排比较函数
*/
int cmp(const void *a,const void *b){
struct edge_node *c = (edge_node *)a;
struct edge_node *d = (edge_node *)b;
if (c->point2!=d->point2)
return c->point2-d->point2;
else
return c->point1-d->point1;
}
/*
*以下子程序使用叉积来求两线段是否相交
*/
int dblcmp(double d){
if (fabs(d)<0.000001)
return 0;
return (d>0)?1:-1;
}
double det(double x1,double y1,double x2,double y2){
return x1*y2-x2*y1;
}
double cross(point_node a,point_node b,point_node c){
return det(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
bool segment_cross_simple(point_node a,point_node b,point_node c,point_node d,int flag){
if (flag==2){
if (dblcmp(det(a.x-b.x,a.y-b.y,c.x-d.x,c.y-d.y))==0)
return true;
else
return
(((dblcmp(cross(a,c,d))^dblcmp(cross(b,c,d)))==-2)&&
((dblcmp(cross(c,a,b))^dblcmp(cross(d,a,b)))==-2));
}
else{
return
(((dblcmp(cross(a,c,d))^dblcmp(cross(b,c,d)))==-2)&&
((dblcmp(cross(c,a,b))^dblcmp(cross(d,a,b)))==-2));
}
}
/*
*the most important function
*用来判断该边能否被看到
*/
bool watch(double x1,double y1,double x2,double y2,int edge_rank){
if ((abs(x1-x2)<0.013 && abs(x1-x2)!=0)||
(abs(y1-y2)<0.013 && abs(y1-y2)!=0))
return false;
bool watch_it=true;
bool watch_it_another=true;
point_node mid,mid_another;
mid.x=(x1+x2)/2;
mid.y=(y1+y2)/2;
if (x1-x2!=0){
double dxy=(y1-y2)/(x1-x2);
mid_another.x=mid.x+0.0001;
mid_another.y=mid.y+dxy/10000;
}
else{
mid_another=mid;
}
for (int i=0;i<edge_num;i++){
if (segment_cross_simple(viewer,mid,point[edge[i].point1],point[edge[i].point2],2))
watch_it=false;
if (segment_cross_simple(viewer,mid_another,point[edge[i].point1],point[edge[i].point2],2))
watch_it_another=false;
}
if (watch_it && watch_it_another)
return true;
else
return (watch(x1,y1,mid.x,mid.y,edge_rank) ||
watch(mid.x,mid.y,x2,y2,edge_rank));
}
/*
*主过程
*/
int main(){
/*
*输入
*/
fin >> point_num;
fin >> viewer.x >> viewer.y;
//读入每个顶点
for (int i=0;i<point_num;i++)
fin >> point[i].x >> point[i].y;
point[point_num]=point[0];
//将顶点转化成边
edge_num=point_num;
for (int i=0;i<point_num-1;i++){
edge[i].point1=i;
edge[i].point2=i+1;
}
edge[point_num-1].point1=0;
edge[point_num-1].point2=point_num-1;
/*
*判断每两条边是否相交
*使用解析几何方法,计算行列式
*/
for (int i=0;i<point_num;i++)
for (int j=0;j<point_num;j++)
if (i!=j)
if (segment_cross_simple(point[i],point[i+1],point[j],point[j+1],1)){
fout << i << " " << j << endl;
fout << "NOFENCE" << endl;
return 0 ;
}
/*
*扫每一条边的中点,
*判断从观察者的视角到这个点是否与其他边相交
*如果与其他边相交了,那么将该边二分,分别求它的中点是否相交
*/
for (int i=0;i<edge_num;i++){
double x1=point[edge[i].point1].x;
double x2=point[edge[i].point2].x;
double y1=point[edge[i].point1].y;
double y2=point[edge[i].point2].y;
if (watch(x1,y1,x2,y2,i)){
edge_watch[edge_watch_num]=edge[i];
edge_watch_num++;
}
}
/*
*对于可视边进行排序
*并对可视边进行输出
*/
qsort(edge_watch,edge_watch_num,sizeof(edge_node),cmp);
fout << edge_watch_num << endl;
for (int i=0;i<edge_watch_num;++i){
fout << point[edge_watch[i].point1].x << " "
<< point[edge_watch[i].point1].y << " "
<< point[edge_watch[i].point2].x << " "
<< point[edge_watch[i].point2].y << endl;
}
return 0;
}
首先要判断该多边形是否存在两条边规范相交的(交点非线段顶点),这可以用叉积搞一下,黑书上讲的听详细的。
然后去判断每条边是否能够被看到,一开始想到的思路是判断该边上是否右两个点可以被看到,这样子可以用二分,然后就不停的搞,代码里有注释,目测边界条件设置位0.013可以勉强跑过,但是设置0.01就挂掉了,可以说被题虐了。