题解 UVA1193 【Radar Installation】

· · 题解

传送门

题意

对于第 i 组测试数据,在一条数轴上选一些点,用以这些点为圆心作的半径为 d 的圆覆盖所给点,若无法全部覆盖,则输出 Case i: -1;否则输出 Case i:+ 最少需要的点数 ans

分析

为了方便,我们把 xy 都存在结构体 asdf 中。

struct asdf
{
    int x,y;
    double l,r;//这个后面会讲
}a[N];

我们首先判断是否能全部覆盖,一个点能够被数轴上的某个点覆盖,当且仅当它与数轴的距离 a_i.y 不超过半径 d,所以我们输入的时候判断一下就好了。

t++;//第t组测试数据 
b=0;//b代表是否有点没被覆盖 
for(int i=1;i<=n;i++)
{
    cin>>a[i].x>>a[i].y;
    if(a[i].y>d)//y就是第i个点到数轴的距离
    {
        b=1;//标记 
        break;//已经有不能覆盖的点就不做无用的输入了 
    }
}
if(b)//有没被覆盖的点 
{
    cout<<"Case "<<t<<": -1"<<endl;
    continue;
}

接下来,我们预处理出距离第 i 个点不超过 d 的最左点 a_i.l 和最右点 a_i.r,这样当我们想判断某个点上装的雷达时候能覆盖到第 i 个点的时候,只要判断是否在 a_i.la_i.r 的线段上就行了。

那么 a_i.l 怎么求呢?请看下图:

如图所示,d、a_i.y(a_i.x-a_i.l) 正好构成了一个直角三角形,根据小学时学过的勾股定理

a_i.y^2+(a_i.x-a_i.l)^2=d^2

反过来就是:

a_i.l=a_i.x-\sqrt{d^2+a_i.y^2} $$a_i.r=a_i.x+\sqrt{d^2+a_i.y^2}$$ ```cpp for(int i=1;i<=n;i++) { a[i].l=a[i].x-sqrt(d*d-a[i].y*a[i].y); a[i].r=a[i].x+sqrt(d*d-a[i].y*a[i].y); } ``` 然后就是**贪心**了,设上一个雷达的位置为 $w$,我们按 $x$ 轴**从小到大**排序,当前我们在第 $i$ 个点,则我们有以下 $2$ 种选择: $1.$ 让当前的点被之前的雷达覆盖。 $2.$ **新建**一个雷达供它使用。 我们为了使雷达最少,在条件允许的情况下应该**尽量选择**第一种,条件就是当前雷达位置 $w$ 比**不少于**能覆盖第 $i$ 个点的最左点 $a_i.l$,即 $w>=a_i.l$,不过有可能当前雷达位置 $w$ **超过**了能覆盖第 $i$ 个点的最右点 $a_i.r$,我们只能把雷达向左移一些满足要求,即 $w=\min(w,a_i.r)$。 如果不能选择第一种,那么我们只能选择第二种了,为了给后面**留出更多的位置**,我们把雷达**尽量往右放**,又要覆盖这个点,所以只能放在能覆盖第 $i$ 个点的最右点 $a_i.r$,即 $w=a_i.r$,且 $ans$ 要加 $1$,因为我们又放了一个雷达。**注意**,第一个点时还没有雷达,所以我们在第一个点直接放雷达,然后循环 $2-n$。 ```cpp sort(a+1,a+n+1,cmp);//排序 w=a[1].r;ans=1;//第一个点直接放雷达 for(int i=2;i<=n;i++)//循环剩下的n-1次 { if(w>=a[i].l)//能覆盖到 w=min(w,a[i].r);//第一种选择 else ans++,w=a[i].r;//否则只能第二种选择 } ``` ## 代码 ```cpp #include<bits/stdc++.h> using namespace std; const int N=1010; int t,n,d,ans; bool b; double w; struct asdf { int x,y; double l,r; }a[N]; bool cmp(asdf p,asdf q)//按x轴从小到大排序 { return p.x<q.x; } int main() { while(cin>>n>>d&&n&&d)//有多组数据 { t++;//第t组测试数据 b=0;//b代表是否有点没被覆盖 for(int i=1;i<=n;i++) { cin>>a[i].x>>a[i].y; if(a[i].y>d)//y就是第i个点到数轴的距离 { b=1;//标记 break;//已经有不能覆盖的点就不做无用的输入了 } } if(b)//有没被覆盖的点 { cout<<"Case "<<t<<": -1"<<endl; continue; } for(int i=1;i<=n;i++) { a[i].l=a[i].x-sqrt(d*d-a[i].y*a[i].y); a[i].r=a[i].x+sqrt(d*d-a[i].y*a[i].y); } sort(a+1,a+n+1,cmp);//排序 w=a[1].r;ans=1;//第一个点直接放雷达 for(int i=2;i<=n;i++)//循环剩下的n-1次 { if(w>=a[i].l)//能覆盖到 w=min(w,a[i].r);//第一种选择 else ans++,w=a[i].r;//否则只能第二种选择 } cout<<"Case "<<t<<": "<<ans<<endl; } return 0; } ``` 懂了来领[双倍经验](https://www.luogu.com.cn/problem/P1325)。