usaco3.4.1

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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章