hunnu 11264 A Classic Myth: Flatland Superhero (凸包)

http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11264

题意:
给你一些点,求一个最小面积的平行四边行使得所有的点都在平行四边形内
思路:
首先凸包求出最外围的点,如果有个平行四边形包含了这些点,则一定会包含所有点。
即然是求最小,则最优解至少有两边与凸包边共线(具体也不知道怎么证明。。。),
所以枚举平行四边形两邻边,形状就确定了。o(n^2)复杂度。

我的代码:

 

/*
program: hunnu_11264
author:BlackAndWhite
*/ 
#include<stdio.h>
#include<math.h>
#include<algorithm>
#define eps 1e-7
using namespace std;
struct Lpoint{double x,y;}a[1001],res[2001],mp[1001];
//a为原始数据,res凸包,mp为离当前线段最远的点 
struct Ldir{double dx,dy;}; 
struct Lline{Lpoint p;Ldir dir;}; 
int i,j,t,n,nr,js=1;
//nr为凸包点数 
double ans;
bool mult(Lpoint sp,Lpoint ep,Lpoint op) 
{
    return (sp.x-op.x)*(ep.y-op.y)>=(ep.x-op.x)*(sp.y-op.y);
}
bool operator < (const Lpoint &l,const Lpoint &r)
{
    return l.y<r.y||(l.y==r.y&&l.x<r.x);
}
double xmulti(Lpoint p1,Lpoint p2,Lpoint p0)
{
    return ((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
}
int graham(Lpoint pnt[],int n,Lpoint res[])
{
    int i,len,k=0,top=1;
    sort(pnt,pnt+n);
    if(n==0) return 0;res[0]=pnt[0];
    if(n==1) return 1;res[1]=pnt[1];
    if(n==2) return 2;res[2]=pnt[2];
    for(i=2;i<n;i++)
    {
        while(top&&mult(pnt[i],res[top],res[top-1]))
            top--;
        res[++top]=pnt[i];
    }
    len=top;res[++top]=pnt[n-2];
    for(i=n-3;i>=0;i--)
    {
        while(top!=len&&mult(pnt[i],res[top],res[top-1])) top--;
        res[++top]=pnt[i];
    }
    return top;
}
int pdcross(Lpoint a,Lpoint b,Lpoint c,Lpoint d)
{
    //判断是否相交 
    double A=(b.x-a.x)*(d.y-c.y),B=(b.y-a.y)*(d.x-c.x);
    if(A-B<eps&&A-B>-eps) return 1;
    return 0;
}
void format(Lline ln,double& A ,double& B,double& C)
{
    A=ln.dir.dy;
    B=-ln.dir.dx;
    C=ln.p.y*ln.dir.dx-ln.p.x*ln.dir.dy;
}
double p2lndis(Lpoint a,Lline ln)
{
    //点到直线距离 
    double A,B,C;
    format(ln,A,B,C);
    return (fabs(A*a.x+B*a.y+C)/sqrt(A*A+B*B));
}
Lpoint ln2ln(Lpoint a,Lpoint b,Lpoint c,Lpoint d)
{
    //直线相交 
    double A1,B1,C1,A2,B2,C2;
    Lpoint z;
    Lline ln1,ln2;
    ln1.p=a;
    ln1.dir.dx=b.x-a.x;
    ln1.dir.dy=b.y-a.y;
    ln2.p=c;
    ln2.dir.dx=d.x-c.x;
    ln2.dir.dy=d.y-c.y;
    format(ln1,A1,B1,C1);
    format(ln2,A2,B2,C2);
    z.x=(C1*B2-C2*B1)/(A2*B1-A1*B2);
    z.y=(C2*A1-C1*A2)/(B1*A2-B2*A1);
    return z;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%lf%lf",&a[i].x,&a[i].y);
        nr=graham(a,n,res);
        //凸包 
        for(i=nr;i<=2*nr;i++)
            res[i]=res[i-nr];
        //复制一遍,方便些 
        j=2;
        double tmax,tt;
        int z=0;
        tmax=0;
        for(i=0;i<nr;i++)
        {
            //求离直线x[i],x[i+1]最远的点 
            tmax=0;
            for(;;j++)
            {
                Lline ln0;
                ln0.p=res[i];
                ln0.dir.dx=res[i+1].x-res[i].x;
                ln0.dir.dy=res[i+1].y-res[i].y;
                tt=p2lndis(res[j],ln0);
                if(tmax<tt+eps) 
                {
                    tmax=tt;
                    mp[i]=res[j];
                }
                else{j--;break;}
            }
        }
        ans=1e9;
        for(i=0;i<nr;i++)//枚举第一条边 
        {
            for(j=i+1;j<nr;j++)//枚举第二条边 
                if(!pdcross(res[i],res[i+1],res[j],res[j+1]))
                {
                    Lpoint cr[3],tmpi,tmpj;
                    tmpi.x=mp[i].x+(res[i].x-res[i+1].x);
                    tmpi.y=mp[i].y+(res[i].y-res[i+1].y);
                    tmpj.x=mp[j].x+(res[j].x-res[j+1].x);
                    tmpj.y=mp[j].y+(res[j].y-res[j+1].y);
                    //下面是四边形的三个顶点 
                    cr[0]=ln2ln(res[i],res[i+1],res[j],res[j+1]); 
                    cr[1]=ln2ln(mp[i],tmpi,res[j],res[j+1]);
                    cr[2]=ln2ln(mp[j],tmpj,res[i],res[i+1]);
                    tt=fabs(xmulti(cr[0],cr[1],cr[2]));
                    if(ans>tt) ans=tt;
                }
        }
        printf("Swarm %d Parallelogram Area: %.4lf\n",js++,ans);
    }
    return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值