zoukankan      html  css  js  c++  java
  • [HNOI2007]最小矩形覆盖

    嘟嘟嘟


    这道题我从昨天晚上5点做到今天下午3点半……差点就疯了。
    真是一道计算几何好题呀!?


    刚开始我以为矩形与坐标轴平行,感觉省选题竟然这么水。但是看完样例后发现我错了……


    首先都知道要求凸包。写代码的时候一定要非常谨慎。对于重合或共线的点都要从栈中弹去,否则在后面的求矩形面积的时候会除以(0),然后就会想我一样(gg)了快(2)个点。
    然后,凭直觉能知道矩形的一个边一定和凸包的一条边平行。那么就能想到用旋转卡壳维护矩形的另外三个最值点(不是顶点),即矩形最上点,最右点,最左点。
    求最上点,和旋转卡壳板子一样,用叉积求面积就行了。
    求最右点,用点积。对于当前直线(AB)和旋转到的点(Ci),如果(overrightarrow{AB} * overrightarrow{C _ i C_{i + 1}} > 0),就接着往下旋转。
    求最左点,和最右点同理。只不过判断条件是(< 0)。而且他的旋转方向和上面相反,所以我又倒着跑了一遍旋转卡壳。


    接下来说怎么求面积。

    看这个图就好了。
    首先矩形的宽很好求,就是(Delta ABC)(AB)为底边的高。用叉积求一遍面积然后除以底边长完事。
    然后求矩形的底边长。我是把他分成了三部分:令(a)表示(AB)的长度,(b)表示(overrightarrow{BD})(overrightarrow{AB})上的投影,(c)表示(overrightarrow{AE})(overrightarrow{AB})上的投影,则底边长(l = |a| + |b| + |c|)
    所以面积求完了啦!


    最后是怎么求矩形的四个顶点。
    沿用上面的字母。
    思路是从右下点(G)开始逆时针一次求。
    (G = B + overrightarrow{AB} * frac{|overrightarrow{BG}|}{|overrightarrow{AB}|})
    接下来求(H)。原来我是这么求的:(H = G + overrightarrow{GD} * frac{|overrightarrow{GH}|}{|overrightarrow{GD}|})。但是会有(G, D)重合的情况。所以就换了种求法,把(overrightarrow{AB})顺时针旋转(90)度得到(overrightarrow{BA'}),然后(H = G + overrightarrow{BA'} * (- frac{|overrightarrow{GH}|}{|overrightarrow{BA'}|})),其中({|overrightarrow{GH}|})就是矩形的高,上一问已经求过。
    对于(I, G)同理,都是向量旋转加伸长缩短,这里就不讲了。
    好像就做完了……
    感觉写了一天的题也不怎么难。还是自己菜啊……


    需要注意的是这题卡精度,(eps)开到(1e-10)我才过。然后如果(|a| < 1e-5),直接输出(0.00000),否则可能会输出(-0.00000)……

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define rg register
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-10;
    const int maxn = 5e4 + 5;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) last = ch, ch = getchar();
      while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n;
    struct Point
    {
      db x, y; int id;     //id跟题目无关,是为了方便调试用的
      Point operator - (const Point& oth)const
      {
        return (Point){x - oth.x, y - oth.y, 0};
      }
      Point operator + (const Point& oth)const
      {
        return (Point){x + oth.x, y + oth.y, 0};
      }
      db operator * (const Point& oth)const
      {
        return x * oth.y - oth.x * y;
      }
      db operator ^ (const Point& oth)const  //这是点积,不是位运算……
      {
        return x * oth.x + y * oth.y;
      }
      Point operator * (db d)
      {
        return (Point){x * d, y * d, 0};
      }
      friend inline db dis(const Point& A)
      {
        return A.x * A.x + A.y * A.y;
      }
      friend inline Point rot(const Point& A)    //顺时针旋转90度
      {
        return (Point){A.y, -A.x};
      }
    }p[maxn], S;
    
    bool cmp(Point& A, Point& B)
    {
      db s = (A - S) * (B - S);
      if(fabs(s) > eps) return s > eps;
      return dis(A - S) < dis(B - S) - eps;
    }
    int st[maxn], top = 0;
    void Graham()
    {
      int id = 1;
      for(int i = 2; i <= n; ++i)
        if(p[i].x < p[id].x - eps || (fabs(p[i].x - p[id].x) < eps && p[i].y < p[id].y - eps)) id = i;
      if(id != 1) swap(p[id].x, p[1].x), swap(p[id].y, p[1].y), swap(p[id].id, p[1].id);
      S.x = p[1].x, S.y = p[1].y;
      sort(p + 2, p + n + 1, cmp);
      st[++top] = 1;
      for(int i = 2; i <= n; ++i)
        {
          while(top > 1 && (p[st[top]] - p[st[top - 1]]) * (p[i] - p[st[top - 1]]) < eps) top--;
          st[++top] = i;
        }
    }
    
    struct Rec
    {
      Point R, U, L;
    }r[maxn];
    db area(Point A, Point B, Point C)
    {
      return (B - A) * (C - A);
    }
    db pho(Point A, Point B, Point C, Point D)
    {
      return (B - A) ^ (D - C);
    }
    inline int nxt(int x)
    {
      if(++x > top) x = 1;
      return x;
    }
    void rota1()
    {
      for(int i = 1, j = 3, k = 2; i <= top; ++i)
        {
          while(nxt(j) != i && area(p[st[i]], p[st[i + 1]], p[st[j]]) < area(p[st[i]], p[st[i + 1]], p[st[nxt(j)]]) - eps) j = nxt(j);
          r[i].U = p[st[j]];
          while(nxt(k) != i && pho(p[st[i]],  p[st[i + 1]], p[st[k]], p[st[nxt(k)]]) > eps) k = nxt(k);
          r[i].R = p[st[k]];
        }
    }
    inline int pre(int x)
    {
      x--;
      if(!x) x = top;
      return x;
    }
    void rota2()   //单独维护最左点
    {
      for(int i = top, j = top; i; --i)
        {
          while(pre(j) != i + 1 && pho(p[st[i]], p[st[i + 1]], p[st[j]], p[st[pre(j)]]) < eps) j = pre(j);
          r[i].L = p[st[j]];
        }
    }
    
    Point Ans[5];
    
    int main()
    {
      n = read();
      for(int i = 1; i <= n; ++i) scanf("%lf%lf", &p[i].x, &p[i].y);
      for(int i = 1; i <= n; ++i) p[i].id = i;
      Graham(); st[top + 1] = st[1];
      rota1(); rota2();
      db ans = (db)INF * (db)INF;
      for(int i = 1; i <= top; ++i)
        {
          //printf("Line:%d %d R:%d U:%d L:%d
    ", p[st[i]].id, p[st[i + 1]].id, r[i].R.id, r[i].U.id, r[i].L.id);
          Point AB = p[st[i + 1]] - p[st[i]];
          db a = sqrt(dis(AB));
          db b = (AB ^ (r[i].L - p[st[i]])) / a, c = (AB ^ (r[i].R - p[st[i + 1]])) / a;
          b = fabs(b), c = fabs(c);
          db l = a + b + c;
          db h = AB * (r[i].U - p[st[i]]) / a;
          h = fabs(h);
          db s = l * h;
          //printf("**%.3lf %.3lf %.3lf %.3lf %.3lf", a, b, c, l, h);
          //printf("@%.3Lf
    ", s);
          if(s < ans - eps)
    	{
    	  ans = s;
    	  Ans[0] = p[st[i + 1]] + AB * (c / a);
    	  Ans[1] = Ans[0] + rot(AB) * (-h / a);
    	  Ans[2] = Ans[1] + rot(Ans[1] - Ans[0]) * (-l / h);
    	  Ans[3] = Ans[2] + rot(Ans[2] - Ans[1]) * (-h / l);
    	  /*Ans[1] = Ans[0] + (r[i].R - Ans[0]) * (h / dis(Ans[0] - r[i].R));  //这就是我原来的写法,点重合会$gg$
    	  Ans[2] = Ans[1] + (r[i].U - Ans[1]) * (l / dis(r[i].U - Ans[1]));
    	  Ans[3] = Ans[2] + (r[i].L - Ans[2]) * (h / dis(r[i].L - Ans[2]));*/
    	}
        }
      printf("%.5lf
    ", ans);
      int id = 0;
      for(int i = 1; i < 4; ++i)
        if(Ans[i].y < Ans[id].y - eps || (fabs(Ans[i].y - Ans[id].y) < eps && Ans[i].x < Ans[id].x - eps)) id = i;
      for(int i = 0; i < 4; ++i)
        {
          db x = Ans[(id + i) % 4].x, y = Ans[(id + i) % 4].y;
          if(fabs(x) < 1e-5) x = 0;
          if(fabs(y) < 1e-5) y = 0;
          printf("%.5lf %.5lf
    ", x, y);
        }
      return 0;
    }
    
  • 相关阅读:
    jQuery给动态添加的元素绑定事件的方法
    无法加载父级样式或设置IIS的asp站点启用父路径
    ASP.NET MVC 必须设置 ErrorMessageString 或 ErrorMessageResourceName,但不能同时设置二者。
    layer弹窗监控键盘事件
    js 刷新windows.open另一个窗口页面或window.open的页面如何刷新(父页面)上层页面
    jquery提交表单,回调函数
    iis Server Error in '/' Application
    jni&&jvmti&&JMC
    Backpropagation
    k-means-algorithm
  • 原文地址:https://www.cnblogs.com/mrclr/p/10001762.html
Copyright © 2011-2022 走看看