UVa 819- Gifts Large and Small

感谢vfk几个月前提供的思路………

提示:(详解在代码后)

  • 整个包装肯定要包围凸包
  • 可以试试长方形的两条边都是平行的 , 所以联想到某一种转来转去的东西
  • 如果知道了一条边的Vector , 其实两对Vector 都知道了
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <deque>
#include <algorithm>
#include <list>
#include <queue>

using namespace std;
const int maxn = 110;

const double eps = 1e-10;
const double pi = acos(-1);

int dcmp(double a) { return fabs(a)<eps?0:(a<0?-1:1); }

struct points
{
    double x , y;
    void read(){ scanf("%lf%lf",&x,&y); }
    points(double x = 0 , double y = 0):x(x),y(y){}
    bool operator <(const points& b)const { return dcmp(x-b.x)==-1 || (dcmp(x-b.x)==0 && dcmp(y-b.y)==-1); }
    bool operator ==(const points& b)const { return dcmp(x-b.x)==0 && dcmp(y-b.y)==0; }
};

typedef points Vector;
typedef vector<points> polygon;

const Vector seaLevel = Vector(10 , 0);

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 b) { return Vector(a.x*b , a.y*b); }
Vector operator /(Vector a , double b) { return Vector(a.x/b  , a.y/b); }
Vector Rotate(Vector a , double rad) { return Vector(a.x*cos(rad)-a.y*sin(rad) , a.x*sin(rad)+a.y*cos(rad)); }
double Dot(Vector a , Vector b) { return a.x*b.x+a.y*b.y; }
double Cross(Vector a , Vector b) { return a.x*b.y-a.y*b.x; }
double Length(Vector a) { return sqrt(Dot(a , a)); }
double angle(Vector a) { return atan2(a.y , a.x); }
//double angle(Vector a , Vector b) 
//{ double hi = Dot(a , b)/Length(a)/Length(b); if(dcmp(fabs(hi)-1)==0) return hi<0?acos(-1):acos(1); return acos(hi); }
double angle(Vector a , Vector b)  { return atan2(Cross(a, b) , Dot(a, b)); }

double turnAngle(Vector a , Vector b) { return dcmp(Dot(a , b))==1?angle(a , b):pi+angle(a , b); }
points ch[maxn];

int convexHull(polygon& p)
{
    sort(p.begin() , p.end());
    p.erase(unique(p.begin() , p.end()) , p.end());
    int n = p.size();
    int m = 0;

    for(int i=0;i<n;i++)
    {
        while(m>1 && dcmp(Cross(ch[m-1]-ch[m-2] , p[i]-ch[m-2]))<0) m--;
        ch[m++] = p[i];
    }

    int k = m;

    for(int i=n-2;i>=0;i--)
    {
        while(m>k && dcmp(Cross(ch[m-1]-ch[m-2] , p[i]-ch[m-2]))<0) m--;
        ch[m++] = p[i];
    }
    if(n>1) m--;
    return m;
}

double distanceOfPointsAndLine(points a ,points b , points c) { return fabs(Cross(b-a , c-a)/Length(b-c)); }

double Area(int a , int b , int c , int d , Vector ab , Vector cd)
{
    double h1 = distanceOfPointsAndLine(ch[a] , ch[b] , ch[b]+ab);
    double h2 = distanceOfPointsAndLine(ch[c] , ch[d] , ch[d]+cd);
    return h1*h2;
}

int main()
{
// freopen("in","r",stdin);
    int n , Case=0;
    while(cin>>n && n)
    {
        points now;
        polygon po;
        for(int i=0;i<n;i++)
        {
            now.read();
            po.push_back(now);
        }
        int m = convexHull(po);
// cout<<m<<endl;
        ch[m] = ch[0];

        double Min = 1e20 , Max = -1;
        double Minx=ch[0].x , Miny=ch[0].y , Maxx=ch[0].x , Maxy=ch[0].y;
        int p1=0 , p2=0 , p3=0 , p4=0;
        Vector v1 , v2 , ori;
        ori = v1 = Vector(1,0);
        v2 = Vector(0,1);

        for(int i=1;i<m;i++)
        {
            if(dcmp(Minx - ch[i].x)==1) Minx = ch[i].x , p3 = i;
            if(dcmp(Maxx - ch[i].x)==-1) Maxx = ch[i].x , p4 = i;
            if(dcmp(Miny - ch[i].y)==1) Miny= ch[i].y , p1 = i;
            if(dcmp(Maxy - ch[i].y)==-1) Maxy = ch[i].y , p2 = i;
        }


        while(dcmp(Cross(ori , v1))>=0)
        {
            double minRad = 1e20;
            minRad = min(minRad , turnAngle(v1 , ch[p1+1]-ch[p1]));
            minRad = min(minRad , turnAngle(v1*(-1) , ch[p2+1]-ch[p2]));
            minRad = min(minRad , turnAngle(v2*(-1) , ch[p3+1]-ch[p3]));
            minRad = min(minRad , turnAngle(v2 , ch[p4+1]-ch[p4]));
// cout<<minRad<<endl; 
                double l = 0 , r = minRad;
                Min = min(Min , Area(p1 , p2 , p3 , p4 , Rotate(v1 , l) , Rotate(v2 , l)));
                while(dcmp(l-r))
                {
                    double len = (r-l)/3;
                    double midl = l+len;
                    double midr = r-len;

                    if(dcmp(Area(p1 , p2 , p3 , p4 , Rotate(v1 , midl) , Rotate(v2 , midl)) - Area(p1 , p2 , p3 , p4 , Rotate(v1 , midr) , Rotate(v2 , midr)))==1) r = midr;
                    else l = midl;
                }

                Max = max(Max ,Area(p1 , p2 , p3 , p4 , Rotate(v1 , l) , Rotate(v2 , l)));

            v1 = Rotate(v1, minRad);
            v2 = Rotate(v2 , minRad);

            if(dcmp(angle(v1 , ch[p1+1]-ch[p1]))==0) p1 = (p1+1)%m;
            if(dcmp(angle(v1*(-1) , ch[p2+1]-ch[p2]))==0) p2 = (p2+1)%m;
            if(dcmp(angle(v2*(-1) , ch[p3+1]-ch[p3]))==0) p3 = (p3+1)%m;
            if(dcmp(angle(v2 , ch[p4+1]-ch[p4]))==0) p4 = (p4+1)%m;
        }

        printf("Gift %d\nMinimum area = %.3lf\nMaximum area = %.3lf\n\n",++Case, Min, Max);
    }

    return 0;
}

详解:

  • 旋转卡壳 , 对 , 就是它。 这种方法在训练指南上有一道例题 , 其中提到的实现方法很不错。 但我没有办法应用到这个题上…

  • 对于这个题 , 我们需要维护四个量 , 即两对对踵点 , 还有一个此时的角度(任意一个Vector其实都是可以的 , 但这里方便实现用了两个Vector)

  • 这个题需要求最大和最小包围矩形 , 但是旋转角度是一个连续的量 , 也就是说我们不能像处理离散量一样枚举每一种情况。

  • 由于这个矩形的面积由角度这个变量唯一决定 , 而任意一个三角函数在一个区间大小小于 pi 的情形下都是单峰的 , 所以 , 三分吧

BTW:
单峰 , 极值处取到最大值留给大家思考(想想这玩意用的是哪个三角函数啊)

你可能感兴趣的:(uva,计算几何)