
描述
有一块草坪,横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<=10000)个点状的喷水装置,每个喷水装置i喷水的效果是让以它为中心半径为Ri的圆都被润湿。请在给出的喷水装置中选择尽量少的喷水装置,把整个草坪全部润湿。
输入
第一行输入一个正整数N表示共有n次测试数据。
每一组测试数据的第一行有三个整数n,w,h,n表示共有n个喷水装置,w表示草坪的横向长度,h表示草坪的纵向长度。
随后的n行,都有两个整数xi和ri,xi表示第i个喷水装置的的横坐标(最左边为0),ri表示该喷水装置能覆盖的圆的半径。
输出
每组测试数据输出一个正整数,表示共需要多少个喷水装置,每个输出单独占一行。
如果不存在一种能够把整个草坪湿润的方案,请输出0。
样例输入
2
2 8 6
1 1
4 5
2 10 6
4 5
6 5
样例输出
1
2
这个题跟第6题很类似,不过6是随便放置喷水龙头,只要按照半径大小排下来就行,这个题目龙头放置位置已经确定,稍微复杂一些.
思路:
根据每个龙头的x,r,还有草地的w和h,可以计算出(只计算半径比h/2大的龙头,否则不能用)喷水范围与草地边界的2个交点left和right,就将此题转化为一个线段覆盖问题.
用一个map<double,double>记录后,会自动按照left排序.如果第一个的left>0或者最大的right小于w,此题无解,直接输出0.结束.
否则开始外层死循环:
初始化龙头个数ind和rightmax,这个变量用来记录当前可以到达的最远的右端点.然后开始内层循环:内层循环的主要目的是,对于所有alive(可用的龙头,下面解释意义)的龙头,找到可以跟当前的rightmax接起来,并且能够右端最远的那个(因为要数目最小).循环中用一个curmax记录对于当前的rightmax可以到达的最远端.
循环中对于所有left小于rightmax的pair,如果其right不等于0(等于0意味着这个龙头不再alive,这个类似动态规划,来减少搜索的数目,考虑到小于rightmax的搜索过之后,新的rightmax不必再搜索上一次搜过的),并且right大于curmax,更新curmax,然后将当前pair的right置0,让它不再alive.内层循环结束后,如果curmax还是0,说明rightmax不能再增长,这是rightmax<w,输出0,不能覆盖.否则rightmax=curmax,更新rightmax,龙头数目加1后,继续下次外层循环.当外层循环结束,如果龙头个数ind不等于0,就输出ind.
#include <iostream>
#include <map>
#include <cmath>
#include <fstream>
using namespace std;
int main()
{
ifstream cin("data.txt");
int n,i;
int m,w,h,x,r;
double left, right;
map <double,double> lar; // left and right
map <double,double>::iterator it;
double rightmax,curmax;
int ind;
cin >> n;
while (n-->0)
{
lar.clear();
rightmax = 0;
cin >> m >> w >> h;
for (i=0;i<m;i++)
{
cin >> x >> r;
if (r<h/2.0) continue; // cannot be used, jump over
left = x-sqrt(r*r-h*h/4.0); // left point
right = x+sqrt(r*r-h*h/4.0); // right point
lar[left] = right; // add to left-right list
if (right>rightmax) rightmax = right;
}
it = lar.begin();
if ((*it).first>0.0 || rightmax<w) // minimal bigger than 0 or maximal smaller than w
{
cout << 0 << endl;
continue;
}
ind = 0; rightmax = 0.0;
while (rightmax<(double)w)
{
curmax = 0.0;
for (it=lar.begin();it!=lar.end();it++) // find the biggest right end with left<=rightmax
{
if ((*it).second==0.0) continue; // alive one
if ((*it).first<=rightmax) // left<=rightmax
{
if ((*it).second>curmax) // right>curmax
{
curmax = (*it).second; // mark the curmax right end
}
(*it).second = 0.0; // mark it has been checked
}
}
if (curmax==0.0) // rightmax cannot be extended, mission failed
{
cout << 0 << endl;
break;
}
else // update the rightmax, add count
{
rightmax = curmax;
ind++;
if (rightmax>=(double)w) break;
}
}
if (ind>0) cout << ind << endl;
}
}
No comments :
Post a Comment