bzoj1185 [HNOI2007]最小矩形覆盖
1185: [HNOI2007]最小矩形覆盖
Time Limit: 10 Sec Memory Limit: 162 MBSec Special JudgeSubmit: 1945 Solved: 853
[Submit][Status][Discuss]
Description
分析:有一个比较显然的结论:最小的矩形一定有一条边在凸包上.利用旋转卡壳求出对应边的最远点.这两个是比较容易做到的,有点难的是如何求另外两边的最远点.
另外两个点的位置显然是单峰函数,可以利用旋转卡壳的思想.关键就是如何以当前凸包的边为x轴来找最远的点,这里不能单单只找横坐标最大/小的点.利用点积,就能将边投影到凸包的边上去,也就是比较点积的大小.那么思路基本上就出来了:底边固定了,最高点利用叉积(旋转卡壳)来确定,左右两个点用点积来确定.最后求矩形的长和高,利用叉积求高,点积求长.求四个顶点的位置则利用向量的伸缩(比例).
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const double eps = 1e-10; int n,tot; double maxx = 1e12; struct node { double x,y; node() {} node(double _x,double _y) :x(_x),y(_y) {} } e[50010],ans[10],p[50010]; double dist(node a,node b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } double det(node a,node b) //叉积 { return a.x * b.y - a.y * b.x; } double dot(node a,node b) //点积 { return a.x * b.x + a.y * b.y; } node operator + (node a,node b) { return node(a.x + b.x,a.y + b.y); } node operator - (node a,node b) { return node(a.x - b.x,a.y - b.y); } node operator * (node a,double x) { return node(a.x * x,a.y * x); } int three(double x) //三态函数 { if (fabs(x) < eps) return 0; if (x > eps) return 1; return -1; } bool operator < (node a,node b) //输出比较 { if (three(a.y - b.y) == 0) { if (three(a.x - b.x) == -1) return true; return false; } if (three(a.y - b.y) == -1) return true; return false; } bool cmp(node a,node b) { double temp = det(a - e[1],b - e[1]); if (temp != 0) return temp > 0; return dist(a,e[1]) < dist(b,e[1]); } void solve() //求凸包 { int id = 1; for (int i = 1; i <= n; i++) if (three(e[i].x - e[id].x) == -1 || (three(e[i].x - e[id].x) == 0 && three(e[i].y - e[id].y) == -1)) id = i; if (id != 1) swap(e[1],e[id]); sort(e + 2,e + 1 + n,cmp); p[++tot] = e[1]; for (int i = 2; i <= n; i++) { while (tot >= 2 && det(e[i] - p[tot - 1],p[tot] - p[tot - 1]) >= 0) tot--; p[++tot] = e[i]; } } void solve2() { p[tot + 1] = p[1]; int left = 1,right = 1,up = 1; for (int i = 1; i <= tot; i++) { while (three(fabs(det(p[up + 1] - p[i + 1],p[i] - p[i + 1])) - fabs(det(p[up] - p[i + 1],p[i] - p[i + 1]))) >= 0) up = up % tot + 1; //最高点 while (three(dot(p[i + 1] - p[right + 1],p[i] - p[i + 1]) - dot(p[i + 1] - p[right],p[i] - p[i + 1])) >= 0) right = right % tot + 1; //最右边的点 if (i == 1) left = right; while (three(dot(p[i] - p[left + 1],p[i + 1] - p[i]) - dot(p[i] - p[left],p[i + 1] - p[i])) >= 0) left = left % tot + 1; double D = dist(p[i],p[i + 1]); double H = det(p[up] - p[i + 1],p[i] - p[i + 1]) / D; double L = fabs(dot(p[i + 1] - p[i],p[left] - p[i + 1])) / D + fabs(dot(p[i + 1] - p[i],p[right] - p[i + 1])) / D; //底边 if (three(L * H - maxx) == -1) { maxx = L * H; ans[0] = p[i] + (p[i + 1] - p[i]) * ((fabs(dot(p[i + 1] - p[i],p[right] - p[i + 1])) / D + D) / D); ans[1] = ans[0] + (p[right] - ans[0]) * (H / dist(p[right],ans[0])); ans[2] = ans[1] + (p[up] - ans[1]) * (L / dist(p[up],ans[1])); ans[3] = ans[2] + (p[left] - ans[2]) * (H / dist(p[left],ans[2])); } } } int main() { scanf("%d",&n); for (int i = 1; i <= n; i++) scanf("%lf%lf",&e[i].x,&e[i].y); solve(); solve2(); int id = 0; for (int i = 1; i < 4; i++) if (ans[i] < ans[id]) id = i; printf("%.5lf\n",maxx); for (int i = 0; i < 4; i++) printf("%.5lf %.5lf\n",ans[(i + id) % 4].x,ans[(i + id) % 4].y); return 0; }