题意:一个凸边型,目标在凸边型内且最优。问最多删除几个点使目标暴露在新凸边型外面。
思路:二分+半平面相交。

1 #include<cstdio> 2 #include<cmath> 3 #include<vector> 4 #include<algorithm> 5 using namespace std; 6 7 struct Point 8 { 9 double x, y; 10 Point(double x=0, double y=0):x(x),y(y) { } 11 }; 12 13 typedef Point Vector; 14 15 Vector operator + (const Vector& A, const Vector& B) 16 { 17 return Vector(A.x+B.x, A.y+B.y); 18 } 19 Vector operator - (const Point& A, const Point& B) 20 { 21 return Vector(A.x-B.x, A.y-B.y); 22 } 23 Vector operator * (const Vector& A, double p) 24 { 25 return Vector(A.x*p, A.y*p); 26 } 27 double Dot(const Vector& A, const Vector& B) 28 { 29 return A.x*B.x + A.y*B.y; 30 } 31 double Cross(const Vector& A, const Vector& B) 32 { 33 return A.x*B.y - A.y*B.x; 34 } 35 double Length(const Vector& A) 36 { 37 return sqrt(Dot(A, A)); 38 } 39 Vector Normal(const Vector& A) 40 { 41 double L = Length(A); 42 return Vector(-A.y/L, A.x/L); 43 } 44 45 double PolygonArea(vector<Point> p) 46 { 47 int n = p.size(); 48 double area = 0; 49 for(int i = 1; i < n-1; i++) 50 area += Cross(p[i]-p[0], p[i+1]-p[0]); 51 return area/2; 52 } 53 54 // 有向直线。它的左边就是对应的半平面 55 struct Line 56 { 57 Point P; // 直线上任意一点 58 Vector v; // 方向向量 59 double ang; // 极角,即从x正半轴旋转到向量v所需要的角(弧度) 60 Line() {} 61 Line(Point P, Vector v):P(P),v(v) 62 { 63 ang = atan2(v.y, v.x); 64 } 65 bool operator < (const Line& L) const 66 { 67 return ang < L.ang; 68 } 69 }; 70 71 // 点p在有向直线L的左边(线上不算) 72 bool OnLeft(const Line& L, const Point& p) 73 { 74 return Cross(L.v, p-L.P) > 0; 75 } 76 77 // 二直线交点,假定交点惟一存在 78 Point GetLineIntersection(const Line& a, const Line& b) 79 { 80 Vector u = a.P-b.P; 81 double t = Cross(b.v, u) / Cross(a.v, b.v); 82 return a.P+a.v*t; 83 } 84 85 const double eps = 1e-6; 86 87 // 半平面交主过程 88 vector<Point> HalfplaneIntersection(vector<Line>& L) 89 { 90 int n = L.size(); 91 sort(L.begin(), L.end()); // 按极角排序 92 93 int first, last; // 双端队列的第一个元素和最后一个元素的下标 94 vector<Point> p(n); // p[i]为q[i]和q[i+1]的交点 95 vector<Line> q(n); // 双端队列 96 vector<Point> ans; // 结果 97 98 q[first=last=0] = L[0]; // 双端队列初始化为只有一个半平面L[0] 99 for(int i = 1; i < n; i++) 100 { 101 while(first < last && !OnLeft(L[i], p[last-1])) last--; 102 while(first < last && !OnLeft(L[i], p[first])) first++; 103 q[++last] = L[i]; 104 if(fabs(Cross(q[last].v, q[last-1].v)) < eps) // 两向量平行且同向,取内侧的一个 105 { 106 last--; 107 if(OnLeft(q[last], L[i].P)) q[last] = L[i]; 108 } 109 if(first < last) p[last-1] = GetLineIntersection(q[last-1], q[last]); 110 } 111 while(first < last && !OnLeft(q[first], p[last-1])) last--; // 删除无用平面 112 if(last - first <= 1) return ans; // 空集 113 p[last] = GetLineIntersection(q[last], q[first]); // 计算首尾两个半平面的交点 114 115 // 从deque复制到输出中 116 for(int i = first; i <= last; i++) ans.push_back(p[i]); 117 return ans; 118 } 119 120 const int maxn = 50000 + 10; 121 int n; 122 Point P[maxn]; 123 124 // 连续m个点是否可以保证炸到总部 125 bool check(int m) 126 { 127 vector<Line> lines; 128 for(int i = 0; i < n; i++) 129 lines.push_back(Line(P[(i+m+1)%n], P[i]-P[(i+m+1)%n]));//相当于从起点0到((i+m+1)%n)这两点延顺时针方向之间的点被删除。应为向量指向起点0,只有向量左边的点符合要求,右边自然被排除。 130 return HalfplaneIntersection(lines).empty(); 131 } 132 133 int solve() 134 { 135 if(n == 3) return 1; 136 int L = 1, R = n-3, M; // 炸n-3个点一定可以摧毁 137 while(L < R) 138 { 139 M = L + (R-L)/2; 140 if(check(M)) R = M; 141 else L = M+1; 142 } 143 return L; 144 } 145 146 int main() 147 { 148 while(scanf("%d", &n) == 1 && n) 149 { 150 for(int i = 0; i < n; i++) 151 { 152 int x, y; 153 scanf("%d%d", &x, &y); 154 P[i] = Point(x, y); 155 } 156 printf("%d ", solve()); 157 } 158 return 0; 159 }