zoukankan      html  css  js  c++  java
  • HDU3629(凸四边形的个数)

    HDU 3629 计算几何

    题目描述:给你n个点(4~700), 问你能够成多少个不同的凸四边形。

    解题报告:

    暴力的话C(700,4)必然超时,发现,任何一个凹包必然是其中一点在其它3点构成的三角形内。

    然后就考虑,能不能求出所有凹包的个数,然后用总数C(n, 4)减去凹包的个数,就是答案。

    依次枚举每个点i,看看其它点能够成多少个包括点i的三角形,就是以这个点为中心的凹包的个数。

    找三角形也要一定的技巧,也是通过逆向思维:找出有多少个三角形不包括点i,然后用总三角形个数C(n – 1, 3) – 这个个数就是就是以这个点为中心的凹包的个数。

    注意到,如果3个点不能圈住中心点,则必然是存在一条通过中心点的直线,使得这三个点都在直线的同侧。利用这个形式,我们用如下方法寻找三角形:

    1:以中心点为中心,对剩余n-1个点极角排序。

    2:依次处理排序后的n-1个点,对于每一个点i,依次往后扫描,找到第一个点j,是ji的夹角大于180度。

    3:那么点i + 1到点j – 1的所有点都可以和点i构成不包括中心点的三角形。个数是

    C(j – i – 1, 2)

    具体算法如图:

     

    HDU <wbr>3629 <wbr>计算几何 <wbr>2010天津网络预赛 <wbr>计算技巧

    注意到:如果每次找j点都从i点出发的话,那么算法就成了N3的复杂度了。其实每次的j点都是从上个j点之后开始的,降低一维复杂度。

     

             自己以前没有注意到的代码技巧:

             计算极角时,如果是负数(-pi ~ 0),就把它加上2 * pi,这样就把角度统一到了0~2pi。

    另外,向这题顺次统计两个点的夹角时,由于会出现转了一圈的情况不好计算角度,所以在原来数组后面再顺次加上n-1一个点,角度同一加2pi,

    for(int j = 0; j < n - 1; j++) ans[j + n - 1] = ans[j] + 2 * pi;

    这样计算角度直接减就好了。

    代码如下:

    #include <iostream>
    #include <string.h>
    #include <algorithm>
    #include <stdio.h>
    #include <math.h>
    
    using namespace std;
    typedef long long LL;
    const int N = 750;
    const double PI = acos(-1.0);
    
    struct Point
    {
        int x,y;
    };
    
    Point p[N];
    double A[N];
    int n;
    
    double angle(double x,double y)
    {
        double t = y - x;
        if(t < 0) t += 2*PI;
        return t;
    }
    
    LL work()
    {
        LL t1 = (LL)n*(n-1)*(n-2)*(n-3)/24;
    	int t2,t3;
    	for(int i2=0;i2<n;i2++){
    		t2=(n-1)*(n-2)*(n-3)/6;
    	//	int i4=0;
    		for(int i3=0,ans=0;i3<n;i3++){
    			if(i3!=i2)A[ans++]=atan2((double)(p[i3].y-p[i2].y),(double)(p[i3].x-p[i2].x));  
    		}
    		sort(A,A+n-1);
    	/*	 for(int j=0,i=0; i<n-1; i++)  
            {  
                while(j<i+n-1)  
                {  
                    if(angle(A[i],A[j%(n-1)])>PI) break;  
                    j++;  
                }  
                t2 -= (LL)(j-i-1)*(j-i-2)/2;  
            }  
            t1 -= t2;  
    		*/
    	//	int t3=0;
    		for(int kk=0,k2=0;kk<n-1;kk++)//kk是原角
    		{
    			while(k2<kk+n-1){//相当于往后推
    				if(angle(A[kk],A[k2%(n-1)])>PI)break; //注意由于后推,k2要取余
    				k2++;//继续往后找
    			}
    			//这时确定过点kk 和另外两点(kk+1,k2-1);
    			//得到的三角形个数被扣去(C(k2-kk-1),2)
    			t2-=(k2-1-(kk+1)+1)*(k2-1-(kk+1))/2;
    			//接着继续 原角后推直到n-1;注意这里k2在之前基础上找就行了
    		}
    		t1-=t2;
    	}
     
        return t1;
    }
    
    int main()
    {
        int T;
        cin>>T;
        while(T--)
        {
            cin>>n;
            for(int i=0; i<n; i++)
                cin>>p[i].x>>p[i].y;
            cout<<work()<<endl;
        }
        return 0;
    }


    版权声明:本文为博主原创文章,未经博主允许不得转载。

    today lazy . tomorrow die .
  • 相关阅读:
    使用dataInput:DescriptionViewer对输入的数据进行校验
    Nunit2.6.2调试.net4类库
    使用WCF RIA服务支持ASP.NET验证
    偶遇 Lc.exe已退出代码为1
    android程序连接后端web service时,提示:Permission denied
    设置XP系统的自动登录
    DomainDataSource的自动刷新
    安装CentOS时,显示 NET:Registered protocol family 2
    清除SqlServer2008的日志
    http://www.cnblogs.com/KnightsWarrior/archive/2010/08/27/1809739.html(博客主)
  • 原文地址:https://www.cnblogs.com/france/p/4808783.html
Copyright © 2011-2022 走看看