题目大意:平面上有n个点,你的任务是以(0,0)为圆心画一个扇形,至少覆盖其中的k个点,使得该扇形的面积最小
解题思路:先把每个点和圆心的距离计算出来,然后再一次排序,统计出以此距离为半径的圆里面有多少个点(第一次剪枝)
如果该半径已经被计算过了就不用再计算了(第二次剪枝)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
#define maxn 5010
#define INF 0x3f3f3f3f
const double pi = acos(-1.0);
int n, k;
struct Point{
int x, y, cnt;
double r, c;
void count() {
r = sqrt(x * x * 1.0 + y * y * 1.0);
c = atan2(y,x);
cnt = 1;
}
}p[maxn], que[maxn];
int cmpR(const Point a, const Point b) {
return a.r < b.r;
}
int cmpC(const Point a, const Point b) {
return a.c < b.c;
}
double solve() {
if(k == 0)
return 0;
double ans = INF;
set<double> s;
sort(p,p+n,cmpC);
for(int i = 0; i < n; i++) {
if(p[i].cnt < k)
continue;
double R = p[i].r, C = 2 * pi;
if(s.find(R) != s.end())
continue;
s.insert(R);
int tmp = 0;
for(int j = 0; j < n; j++)
if(p[j].r < R || fabs(p[j].r - R) < 1e-9) {
que[tmp++] = p[j];
if(tmp >= k)
C = min(C,que[tmp-1].c-que[tmp-k].c);
}
if(tmp < k)
continue;
for(int j = 0; j < k - 1; j++)
C = min(C,que[j].c - que[tmp+j-k+1].c + 2 * pi);
ans = min(ans,R * R * C / 2);
}
return ans;
}
int main(){
int mark = 1;
while(scanf("%d%d",&n, &k) == 2 && n + k) {
for(int i = 0; i < n; i++) {
scanf("%d%d",&p[i].x, &p[i].y);
p[i].count();
}
sort(p,p+n,cmpR);
for(int i = 1; i < n; i++)
p[i].cnt += p[i-1].cnt;
printf("Case #%d: %.2lf\n",mark++, solve());
}
return 0;
}