Hills - UVa 1359 几何

39 篇文章 0 订阅

Little Billy likes drawing with chalks of all colors. As a child of his age, all he can do is to draw straight lines. Today he has been told that hills are of the shape of triangles, and he brought out a lot of his works and tried to identify as many hills as he could. Being one of his best friends, you gladly joined his effort. However after working for a while you are desperate to find the huge number of sketches he has produced. You suggested this alternative plan - to code up a program for him.

\epsfbox{p3491.eps}

This sample figure only contains a single ``hill"

Keep in mind that in Billy's pictures all the lines are connected, and no line will have an end point right on another line. We would only count the hills that are not cut by other lines.

Input 

Standard input will contain multiple test cases. The first line of the input is a single integer T (1$ \le$T$ \le$10)which is the number of test cases. T test cases follow, each preceded by a single blank line.

Each test case starts with an integer N (1 < N$ \le$500) , which is the number of lines on the picture. The next Nlines contain a line segment's two end points. The coordinates are all integers (- 10, 000 < xy < 10, 000) .

Output 

Results should be directed to standard output. Start each case with ``Case # :" on a single line, where # is the case number starting from 1. Two consecutive cases should be separated by a single blank line. No blank line should be produced after the last test case.

For each test case, print the number of hills on a single line.

Sample Input 

3

3
-1 0 3 0
0 -1 0 3
-1 3 3 -1

4
-3 0 3 0
-3 -1 1 3
3 -1 -1 3
0 3 0 -1

6
-3 -2 3 -2
-2 -3 -2 3
-3 2 3 2
2 -3 2 3
-3 -3 3 3
1 -1 -1 1

Sample Output 

Case 1:
1

Case 2:
2

Case 3:
0

题意:在给定的线段中,能形成多少个完整的三角形,即该三角形中不存在其他的线段的边。给定的线段的端点不会出现自其他线段上,另外所有的线段都相连。

思路:AC得很奇怪,感觉思路不太对。因为数据量是500,所以应该不能枚举三角形后再逐一判断是否合法,(虽然我没有尝试,但是感觉时间复杂度太大了,而且也是不好判断是否合法)。

          首先预处理出所有交点,每条边以第一个端点作为基准,用set存储所有和其他边的交点到该端点的距离,再加上这两个端点。i,j,k枚举三条边,如果能构成三角形的话,判断是否合法。因为给定的线段的端点不会出现在线段上,假设i边和j,k的交点分别为A,B,可以知道AB到i线段第一个端点的距离,那么如果set[i]中有在这两个距离中间的,就说明有一条线段破坏了这个三角形。

          第一发WA了,然后感觉set可能有点问题,就加了个判断,如果距离等于A的距离指针后移再判断。又交一发,没judge完就发现了另一个问题。假设对于当前的一个三角形,如果三角形的一个顶点恰好在另外一条线段上,那么这个线段会不会破坏这个三角形还要考虑这个线段和三角形的角度问题。但是它竟然AC了。。。感觉后面这个问题的判断又要加好多东西,算了,就这样。去UVa上一看,之前只有4个人过,我也是给醉了。

奇怪的AC代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
const double eps=1e-10;
const double PI=acos(-1.0);
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);}
Vector operator * (Vector A,double p){return Vector(A.x*p,A.y*p);}
Vector operator / (Vector A,double p){return Vector(A.x/p,A.y/p);}
int dcmp(double x){return (x>eps)-(x<-eps);}
bool operator == (const Point &A,const Point &B){return dcmp(A.x-B.x)==0 && dcmp(A.y-B.y)==0;}
double Dot(Vector A,Vector B){return A.x*B.x+A.y*B.y;}
double Length(Vector A){return sqrt(Dot(A,A));}
double Angle(Vector A,Vector B){return acos(Dot(A,B)/Length(A)/Length(B));}
double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}
Point GetLineIntersection(Point P,Vector v,Point Q,Vector w)
{
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}
bool parallel(Point aa,Point ab,Point ba,Point bb)
{
    return dcmp(Cross(aa-ab,ba-bb))==0;
}
bool OnSegment(Point p,Point a1,Point a2)
{
    return dcmp(Cross(a1-p,a2-p))==0 && dcmp(Dot(a1-p,a2-p))<=0;
}

bool cmp(Point a,Point b)
{
    if(dcmp(a.x-b.x)==0)
      return dcmp(a.y-b.y)<=0;
    return dcmp(a.x-b.x)<0;
}
int T,t,n,edge,m,m2;
Point line[510][2],temp,p[510][510];
bool flag[510][510];
set<double> Set[510];
set<double>::iterator iter;
bool check(Point A,Point B,int pos)
{
    double ret1,ret2,k;
    ret1=Length(A-line[pos][0]);
    ret2=Length(B-line[pos][0]);
    if(ret1>ret2)
      swap(ret1,ret2);
    iter=Set[pos].upper_bound(ret1);
    k=*iter;
    while(dcmp(k-ret1)==0)
    {
        iter++;
        k=*iter;
    }
    //printf("pos=%d k=%.2f %.2f %.2f %d\n",pos,k,ret1,ret2,dcmp(ret2-k));
    if(dcmp(ret2-k)>0)
      return false;
    return true;
}
int main()
{
    int i,j,k,ans;
    double ret,ret2;
    Point A,B,C;
    scanf("%d",&T);
    for(t=1;t<=T;t++)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
           scanf("%lf%lf%lf%lf",&line[i][0].x,&line[i][0].y,&line[i][1].x,&line[i][1].y);
        memset(flag,0,sizeof(flag));
        for(i=1;i<=n;i++)
        {
            Set[i].clear();
            Set[i].insert(0);
            Set[i].insert(Length(line[i][1]-line[i][0]));
        }
        for(i=1;i<=n;i++)
           for(j=i+1;j<=n;j++)
              if(!parallel(line[i][0],line[i][1],line[j][0],line[j][1]))
              {
                  temp=GetLineIntersection(line[i][0],line[i][1]-line[i][0],line[j][0],line[j][1]-line[j][0]);
                  if(OnSegment(temp,line[i][0],line[i][1]) && OnSegment(temp,line[j][0],line[j][1]))
                  {
                      flag[i][j]=1;
                      p[i][j]=temp;
                      ret=Length(temp-line[i][0]);
                      Set[i].insert(ret);
                      ret=Length(temp-line[j][0]);
                      Set[j].insert(ret);
                  }
              }
        ans=0;
        for(i=1;i<=n;i++)
           for(j=i+1;j<=n;j++)
              if(flag[i][j])
              {
                  A=p[i][j];
                  for(k=j+1;k<=n;k++)
                     if(flag[i][k]&& flag[j][k])
                     {
                         B=p[i][k];
                         C=p[j][k];
                         if(A==B || A==C || B==C)
                           continue;
                         if(check(A,B,i) && check(A,C,j) && check(B,C,k))
                           ans++;
                     }
               }
        if(t!=1)
          printf("\n");
        printf("Case %d:\n%d\n",t,ans);
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值