這道題花了有點時間,不擅長計算幾何,搞了略久,最後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就掛掉了,可以說被題虐了。