LA 4356 Fire-Control System
题目大意:
平面上有n个点,找到一个以(0,0)为圆心的扇形,至少覆盖k个点,使其面积尽可能小.
题目分析:
影响扇形面积的有两个因素:圆心角和半径,但是并不容易同时维护两个变量.
可以选择确定某一变量,使得另一变量尽可能小来使得面积尽可能小.
这里选择先确定半径,那么就会剩下一些点,再在这些点中筛选使其面积尽可能小.
代码:
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const double PI=acos(-1.0);
const int maxn=5000+10;
struct Point {
double k;//(x,y)与(0,0)所成弧度角
int x,y,r2;//r2=r*r=x*x+y*y
bool operator < (const Point& rhs) const {
return k<rhs.k;
}
void input() {
scanf("%d%d",&x,&y);
k=atan2(y,x);r2=x*x+y*y;
}
}P[maxn];
double A[maxn];//存放筛出来的点的角度
int vis[2000010],n,k,kase;//vis判断当前枚举的R2是否已经枚举过了
double solve()
{
if(k==0) return 0;
double ans=1e9;
for(int i=0;i<n;i++) {
int cnt=0,R2=P[i].r2;
if(vis[R2]==kase) continue; vis[R2]=kase;//若当前已经算过了,就不用再算
for(int j=0;j<n;j++) if(P[j].r2<=R2) A[cnt++]=P[j].k;//若能被包括,将其角度放进A中
if(cnt<k) continue;
for(int j=0;j<cnt;j++) {//因为只要k个就满足了,对于某个确定起点j,从j开始逆时针转动,
double now;//当找到k个点时,一定是以j为起点的最小角度,所以对于每个起点j,只需要知道j往后k个的角度
if(j+k-1<cnt) now=A[j+k-1]-A[j];
else now=A[k-(cnt-j)-1]+2*PI-A[j];//若往后又到了开始,要+2π
ans=min(ans,R2*now/2);
}
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&k)==2&&(n||k)) {
for(int i=0;i<n;i++) P[i].input();
if(k) sort(P,P+n); ++kase;
printf("Case #%d: %.2lf\n",kase,solve());
}
return 0;
}