OJ2185.蚂蚁过河

文章讲述了如何使用递归和优化策略解决小蚂蚁过河问题,通过荷叶作为桥梁,通过比较荷叶坐标和河宽,确定是否存在路径帮助蚂蚁到达对岸。关键在于避免重复搜索和利用等价类减少时间复杂度。
摘要由CSDN通过智能技术生成

题目描述

小蚂蚁在外出觅食的时候发现,在河对岸有一块十分诱人的方糖!它非常想跨过这条河,可是它不善水性,只能走陆地,因此无法凭自己到达对岸。

此时,河里的助教听到了它的希望,决定帮助一下小蚂蚁。他在河底发现了 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;
}

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值