题目描述
小蚂蚁在外出觅食的时候发现,在河对岸有一块十分诱人的方糖!它非常想跨过这条河,可是它不善水性,只能走陆地,因此无法凭自己到达对岸。
此时,河里的助教听到了它的希望,决定帮助一下小蚂蚁。他在河底发现了 n 片半径为 r 的 圆形 荷叶,将它们立了起来,如果两片荷叶相交或相切,小蚂蚁就可以从其中一片荷叶走到另一片荷叶;我们假设河的两岸是两条直线,如果荷叶与岸边相交或相切,那么小蚂蚁可以从岸边走到荷叶上、或者从荷叶上走到岸边。我们可以对空间进行建系,小蚂蚁所在的岸边为 y=0 ,河的对岸为 y=w 。
在助教的帮助下,小蚂蚁能够成功过河、吃到方糖吗?
输入格式
总共有 n+1 行输入,数字之间用空格分隔
第一行包含三个整数 n,r,w ,分别代表荷叶的数量、荷叶的半径、河的宽度。
接下来的 n 行,每行包含两个整数 x,y ,代表一个荷叶的圆心坐标为 (x,y)。
输出格式
一个整数,如果小蚂蚁能够到达对岸、则输出 1 ,如果不能,则输出 0
。
样例输入
样例输入 1
2 1 4
1 1
1 3
样例输入 2
3 2 3
0 0
1 0
4 3
样例输出
样例输出 1
1
样例输出 2
0
数据范围
1 ≤ n ≤ 10^3 , 1 ≤ r, w, x, y ≤ 10^4
时空磁盘限制(运行时)
时间限制:1000 ms
内存空间限制:244 MiB
磁盘空间限制:不可使用磁盘
解题思路:
考虑用递归的方法解决此题,,为了使解题过程更加直观易读,不妨先将输入的荷叶坐标按照y的大小升序排列,排列算法随意,堆排序,哈希排序……whatever
递归的起点是从y最小的荷叶开始寻找相连的荷叶(等价类),当荷叶坐标y – r > 0时,便不再视为递归起点,检索剩余荷叶是否与其相连,如果相连则考虑该荷叶的等价类,实现递归,注意:这里不能只检索y更大的荷叶,因为可能出现以下情况:
荷叶1与2不相连,下一个检索的是3,当考虑3的等价类时,如果只考虑y更大的值,那么2就会被忽略,实际上1,2,3是一个等价类,所以检索要遍历所有的荷叶而非y更大的荷叶。
递归的终点为找到一片荷叶的y + r >= w,或者已经走到了最后一片荷叶但是仍未到达对岸。
但是如果每一次都将所有荷叶遍历一次,最坏情况下时间复杂度将达到o(n^2),有没有什么办法优化呢?
我们注意到,如果存在一条通往对岸的路,那么这条路上一定没有“回头路”,也就是说,这条路上的荷叶至多经过一次。
那么,我们可以为每一个荷叶类中添加一个Flag,初始值为0,当我们第一次遍历到时,Flag = 1,当遍历到Flag为1的荷叶时,意味着要不然这条荷叶不属于通往对岸的路上的,要不然就是属于通往对岸的路上但是我们已经走过了,无论如何都应该跳过这片荷叶。
代码实现:
创建荷叶类:
class LotusLeaf
{
public:
bool Flag;
int x, y;
LotusLeaf(int a, int b) : x(a), y(b), Flag(0) {}
};
//我这里用了堆排序
递归寻找路径:
bool Search(int i)
{
if (a[i]->Flag == 1) return 0;//表示这片荷叶已经走过了
if (a[i]->y + r >= w) return 1;
bool flag = 0;
int tmp = i;
a[i]->Flag = 1;
i++;
while (i<n)
{
if (pow(a[tmp]->x - a[i]->x, 2) + pow(a[tmp]->y - a[i]->y, 2) <= pow(2 * r, 2))flag = Search(i);
if (flag == 1) return 1;
i++;
}
i = tmp - 1;
while (i >= 0)
{
if (pow(a[tmp]->x - a[i]->x, 2) + pow(a[tmp]->y - a[i]->y, 2) <= pow(2 * r, 2))flag = Search(i);
if (flag == 1) return 1;
i--;
}
return 0;
}
我用了一个指针数组a[ ]储存对应的荷叶信息,按照y升序排列
判断能否抵达终点:
bool GetWay()
{
int i = 0;
while (i < n && a[i]->y - r <= 0)
{
if (Search(i)) return 1;
i++;
}
return 0;
}
完整代码如下:
#include<iostream>
#include<cmath>
using namespace std;
class LotusLeaf
{
public:
bool Flag;
int x, y;
LotusLeaf(int a, int b) : x(a), y(b), Flag(0) {}
};
class Leaves
{
public:
LotusLeaf** a;
int n, r, w;
Leaves(LotusLeaf**& x,int tn,int tr,int tw):a(x),n(tn),r(tr),w(tw){}
bool Search(int i)
{
if (a[i]->Flag == 1) return 0;//表示这片荷叶已经走过了
if (a[i]->y + r >= w) return 1;
bool flag = 0;
int tmp = i;
a[i]->Flag = 1;
i++;
while (i<n)
{
if (pow(a[tmp]->x - a[i]->x, 2) + pow(a[tmp]->y - a[i]->y, 2) <= pow(2 * r, 2))flag = Search(i);
if (flag == 1) return 1;
i++;
}
i = tmp - 1;
while (i >= 0)
{
if (pow(a[tmp]->x - a[i]->x, 2) + pow(a[tmp]->y - a[i]->y, 2) <= pow(2 * r, 2))flag = Search(i);
if (flag == 1) return 1;
i--;
}
return 0;
}
bool GetWay()
{
int i = 0;
while (i < n && a[i]->y - r <= 0)
{
if (Search(i)) return 1;
i++;
}
return 0;
}
};
void adjust(LotusLeaf** a, int start, int end)
{
LotusLeaf* tmp = a[start];
for (int i = start * 2 + 1; i <= end; i = i * 2 + 1)
{
if (i < end && a[i]->y < a[i + 1]->y)
{
i++;
}
if (a[i]->y > tmp->y)
{
a[start] = a[i];
start = i;
}
else break;
}
a[start] = tmp;
}
void sort(LotusLeaf** a, int len)
{
for (int i = len / 2 - 1; i >= 0; i--)
{
adjust(a, i, len - 1);
}
LotusLeaf* tmp;
for (int i = 0; i < len - 1; i++)
{
tmp = a[0];
a[0] = a[len - 1 - i];
a[len - 1 - i] = tmp;
adjust(a, 0, len - i - 2);
}
}
int main()
{
int n, r, w;
cin >> n >> r >> w;
int x, y;
LotusLeaf** a = new LotusLeaf * [n];
for (int i = 0; i < n; i++)
{
cin >> x >> y;
a[i] = new LotusLeaf(x, y);
}
sort(a, n);
Leaves leaves(a, n, r, w);
cout << leaves.GetWay();
return 0;
}