ACM-ICPC 2018 沈阳赛区网络预赛 E. The cake is a lie(单位圆覆盖问题+二分)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ftx456789/article/details/82532729

You promised your girlfriend a rounded cake with at least SSS strawberries.

But something goes wrong, you don’t have the final cake but an infinite plane with NNN strawberries on it. And you need to draw a circle which contains strawberries completely to make the cake by yourself.

To simplify the problem, the strawberries are represented as circles in 2D plane. Every strawberry has its center (Xi,Yi) and the same radius R.
The cost of your cake is the radius of the circle you draw. So you want to know the minimal cost of the cake, or if you can’t give your girlfriend such a cake.

Input

The input file contains several test cases.

The first line of the file is an integer T, giving the number of test cases. (T≤20)

For each test case, there are two integers NNN and SSS in the first line, denoting the number of total strawberries and the number of strawberries on cake as you promised. (1≤N,S≤300)
In the following NNN lines, each line contains two integers Xi,Yi
​, denoting the coordinates of the strawberry center. (0≤Xi,Yi≤10000)

The next line contains one integer RRR, denoting the radius of all strawberries. (1≤R≤10000)

It’s guaranteed that sum of NNN is smaller than 1000

Output

Output the minimal cost correct to four decimal places if you can make a rounded cake with at least SSS strawberries, or “The cake is a lie.” if not.

样例输入 复制
2
5 3
0 0
0 2
2 0
0 0
1 1
1
1 2
0 0
1

样例输出 复制
1.7071
The cake is a lie.

题意

给你n个点圆的圆心和相同的半径R,求至少覆盖s个小圆的最小大圆半径是多少

思路

这题的前身可以认为是POJ1981,POJ1981讲的是平面上有n个点单位圆最多能覆盖几个点,他是有 O ( n 2 l o g n ) 的做法的,那我们回到这一题,我们先不考虑整圆,只考虑圆心,如果我们求出来的大圆能包含s个圆心那么我们让大圆的半径增加R,那就可以覆盖整个圆了,所以现在的问题就是如何快速的求出对一个大圆的半径有最多能覆盖多少个点

朴素算法:由限制条件可知,最后目标圆圆心一定可由某两点唯一确定(即目标圆的圆弧上一定恰有两个平面点集中的点)。具体实现:C(N,2)枚举任意两圆,构造可行圆,枚举N个点,判断是否在圆上或者圆内(即是否被覆盖)。复杂度O(N^3)。我所知的标准做法:O(n^2*logn)的圆上交点扫描法。
转化为圆上的弧最多被覆盖几次的问题。
直觉可解,圆上某个弧如果被某个圆覆盖过,那么我们把目标圆圆心放在该段弧上时,目标圆一定能把该弧所在圆圆心覆盖(即平面点集中的某点)。
所以,被覆盖次数最多的弧的被覆盖次数即为目标圆(最优)最多能够覆盖的点数。
具体做法:
依旧是C(N,2)枚举任意两点,以BA为例,此时i=1,j=0。
求取BA的极角theta,利用atan2(因变量范围(-pi,pi])。
为方便极角排序,对于负角要+2pi。
B、A构造的圆交于X1,X4两点,求取X1B与BA的夹角和X4B与AB的夹角(相同)。
Phi ,acos(D/2.0/R),D=dis(A,B)。
以上可得出两圆交点在当前圆上对于圆心的极角。
为方便极角排序,每个极角均加2pi,使域扩张到[0,4pi],从而防止了跨0角的出现。
alpha[t].value=theta – phi + 2pi,alpha[t].flag = 1(弧的进入端)
alpha[t+1].value = theta + phi + 2pi,alpha[t+1].flag = false(弧的退出端)。
接下来,对于每个圆上的t(偶数)个极角进行排序,再顺序扫描,动态更新最大值。
for(i,0,t)
if(alpha[i].flag)
sum++
else
sum–
if(sum > ans)
ans = sum
最后输出答案时,输出ans+1即可。(因为要算上弧所在圆自身)

参考这里写链接内容
然后有了这个模板就可以

#define Mn 300
const double eps = 1e-9;
const double pi = acos(-1.0);
#define sqr(x) ((x) * (x))
struct point
{
    double x,y;
    void read()
    {
    scanf("%lf%lf",&x,&y);
    }
    void print()
    {
        printf("%lf%lf\n",x,y);
    }
    double friend dis(const point &a,const point &b)
    {
        return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
    }
} p[Mn + 5];
struct alpha
{
    double v;
    bool flag;
    bool friend operator <(const alpha &a,const alpha &b)
    {
        return a.v < b.v;
    }
} alp[Mn * Mn + 5];
int solve(int n,double R)//半径为R的圆最多能覆盖多少个点
{
    int MAX = 0;
    for(int i = 0; i < n; i++)
    {
        int t = 0;
        for(int j = 0; j < n; j++)
        {
            if(i == j)
                continue;
            double theta,phi,D;
            D = dis(p[i],p[j]);
            if(D > 2.0 * R)
                continue;
            theta = atan2(p[j].y - p[i].y,p[j].x - p[i].x);
            if(theta < 0)
                theta += 2 * pi;
            phi = acos(D / (2.0 * R));
            alp[t].v = theta - phi + 2 * pi;
            alp[t].flag = true;
            alp[t + 1].v = theta + phi + 2 * pi;
            alp[t + 1].flag = false;
            t += 2;
        }
        sort(alp,alp + t);
        int sum = 0;
        for(int j = 0; j < t; j++)
        {
            if(alp[j].flag)
                sum ++;
            else
                sum --;
            if(sum > MAX)
                MAX = sum;
        }
    }
    return MAX+1;
}

就可以二分答案

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define Mn 300
const double eps = 1e-9;
const double pi = acos(-1.0);
#define sqr(x) ((x) * (x))
struct point
{
    double x,y;
    void read()
    {
    scanf("%lf%lf",&x,&y);
    }
    void print()
    {
        printf("%lf%lf\n",x,y);
    }
    double friend dis(const point &a,const point &b)
    {
        return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
    }
} p[Mn + 5];
struct alpha
{
    double v;
    bool flag;
    bool friend operator <(const alpha &a,const alpha &b)
    {
        return a.v < b.v;
    }
} alp[Mn * Mn + 5];
int solve(int n,double R)//半径为R的圆最多能覆盖多少个点
{
    int MAX = 0;
    for(int i = 0; i < n; i++)
    {
        int t = 0;
        for(int j = 0; j < n; j++)
        {
            if(i == j)
                continue;
            double theta,phi,D;
            D = dis(p[i],p[j]);
            if(D > 2.0 * R)
                continue;
            theta = atan2(p[j].y - p[i].y,p[j].x - p[i].x);
            if(theta < 0)
                theta += 2 * pi;
            phi = acos(D / (2.0 * R));
            alp[t].v = theta - phi + 2 * pi;
            alp[t].flag = true;
            alp[t + 1].v = theta + phi + 2 * pi;
            alp[t + 1].flag = false;
            t += 2;
        }
        sort(alp,alp + t);
        int sum = 0;
        for(int j = 0; j < t; j++)
        {
            if(alp[j].flag)
                sum ++;
            else
                sum --;
            if(sum > MAX)
                MAX = sum;
        }
    }
    return MAX+1;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,s;
        scanf("%d%d",&n,&s);
        for(int i=0; i<n; i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        double d;
        scanf("%lf",&d);
        if(s>n)
        {
            printf("The cake is a lie.\n");
            continue;
        }
        double l=0,r=99999999;
        while(fabs(l-r)>eps)
        {
            double mid=(l+r)/2;
            if(solve(n,mid)>=s)
                r=mid;
            else
                l=mid;
        }
        printf("%.4lf\n",l+d);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ftx456789/article/details/82532729