hdu 4353

来源:互联网 发布:电子打鼓软件下载 编辑:程序博客网 时间:2024/05/17 22:15

先简单说说这道题的题意吧,题目讲的大概是个挖金矿的故事,首先分别给出n个普通点(这些点是你可以自己选择的用来圈金矿的点),以及m个金矿所在点。你所需要计算的,就是从n中任意选择x个点(3<=x<=n)组成一个x边形,然后用这个x边形除以x边形内所圈住的金矿的个数,我们暂且将这个比值称之为“性价比”。

好了,题目的要求是输出最高的性价比,那么怎么得到这个性价比呢?首先3000MS的时间虽然足够你暴力的了,但并不意味着真的足够随便暴力枚举,尤其是n的最大取值是200。200个点所能组成的多边形数目是很可怕的。那么,就需要我们仔细分析下题意了....

我们分析下对于一个边数大于三的多边形,它的“性价比”是如何得到的呢?

(每个三角形的面积和)/(每个三角形围住的点的个数和)==(每个三角形的“性价比”之和)/三角形总个数。

也就是说,多边形的性价比来自于每个三角形的性价比的平均值,既然是平均值,那么在所有的三角形中,自然会有比大于平均值、等于平均值和小于平均值的”性价比“存在,那么回头再看看题目要的输出是什么?没错,就是最大的”性价比“,所以最终的结果必然是某个三角形的”性价比“。

也许有人看到这儿会问样例2的问题了。不得不说,值得一提的是这道题的Hint,真的是很误导你不往正确的思路上想。Hint中所取的看似是内个凹四边形“性价比”最高,但其实,它只是恰好等于由(0,0),(0,5),(2,2)或者(0,0),(5,0),(2,2)所组成的三角形的“性价比罢了,而Hint中确有偏偏只给你(0,0),(0,5),(5,0)组成的“性价比”三角形。用心险恶啊~~~

知道了以上内容,这道题唯一的问题就只剩下求每个三角形内金矿个数这一问题了。因为3000MS,所以对于三层for循环O(n^3)遍历所有三角形的个数时间是足够我们“计算几何瞎暴力”的了,但是如果再添一层for循环去遍历m个金矿点在不在该三角形内,时间复杂度就成了可怕的O(n^3*m),超时是必然的了。于是便需要采取一些手段,我的办法是利用叉积来计算对于任意一条有向边AB,计算出它所在直线的右侧的金矿点的个数。并记录在二维数组对应位置record[A][B]中,时间复杂度同样是三次幂的O(n^2*m),但在查询的时候是O(1)的复杂度,查询的方法也很容易理解。

例如:

三角形内部的点的个数=向量AB右侧的点-向量BC右侧的点-向量CA右侧的点。

由此,这道题以总时间复杂度O(n^2*m+n^3)而结束。


#include<cstdio>#include<iostream>#include<algorithm>#include<cmath>#define MIN(a,b) ((a<b)?(a):(b))const double inf = 1e9;const double eps = 1e-6;using namespace std;int n,m;int num[210][510];struct point{    double x,y;    bool operator < (const point &a) const{        return x<a.x;    }}pointn[210],pointm[510];double multi(point p0,point p1,point p2){    return fabs((double)((p1.x - p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y))*0.5);}//计算面积,其实只用一个叉积就够了,但是三角形习惯用这个了int det(point A,point B,point C){    return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);}void In(point a,point b,int &cnt){    int x1=a.x;    int x2=b.x;    for(int i=0;i<m;i++)        if(x1<=pointm[i].x&&pointm[i].x<x2)            if(det(a,b,pointm[i])>0)               cnt++;}int main(){    int t;    scanf("%d",&t);    for(int count=1;count<=t;count++){        double ans=-1;        scanf("%d%d",&n,&m);        for(int i=0;i<n;i++)            scanf("%lf%lf",&pointn[i].x,&pointn[i].y);        sort(pointn,pointn+n);        for(int i=0;i<m;i++)            scanf("%lf%lf",&pointm[i].x,&pointm[i].y);        for(int i=0;i<n;i++)            for(int j=i+1;j<n;j++)                 In(pointn[i],pointn[j],num[i][j]=0);        for(int i=0;i<n;i++)        {            for(int j=i+1;j<n;j++)            {                for(int k=j+1;k<n;k++)                {                      int cnt=num[i][k]-num[i][j]-num[j][k];                      if(cnt==0) continue;                      double area=multi(pointn[i],pointn[j],pointn[k]);                      double tmp=fabs(area/cnt);                      if(ans==-1||tmp<ans) ans=tmp;                }            }        }        if(ans==-1)        printf("Case #%d: -1\n",count);        else        printf("Case #%d: %lf\n",count,ans);    }    return 0;}


0 0
原创粉丝点击