Luogu P2661 信息传递

P2661 信息传递

这题大概是给一个每个点出度都为1的图,求最小的环的长度。

我的思路是直接对每一个点深搜,搜到搜过的点就说明存在环,更新答案。

int n;			// 点数
int p[maxn];	// 传递对象编号
bool vis[maxn];	// 在一次搜索中访问过的点
int dis[maxn];	// 该点到本次搜索起点的距离
int ans = INF;	// 最小环长度

void dfs(int s, int depth)
{
	if (vis[s])	// 存在环
	{
		ans = min(ans, depth - dis[s]);	// 更新答案
	}
	else		// 不存在环就继续搜索
	{
		vis[s] = true;
		dis[s] = depth;
		dfs(p[s], depth + 1);
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &p[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		memset(vis, 0, sizeof(vis));
		memset(dis, 0, sizeof(dis));
		dfs(i, 0);	// 从每个点开始搜索
	}
	printf("%d\n", ans);
	return 0;
}

但是这样显然太暴力了,重复计算了很多点,那么怎么优化呢?

这里可以再开一个数组,记录已经搜索过的点。注意:上面的vis是记录一次搜索内搜过的点,是为了找到环,而这个数组则是用来避免重复的。

/* 增加的部分 */
bool nvis[maxn];

void dfs(int s, int depth)
{
	if (nvis[s])	//之前搜过的
	{
		return;
	}
	if (vis[s])	// 存在环
	{
		ans = min(ans, depth - dis[s]);	// 更新答案
	}
	else		// 不存在环就继续搜索
	{
		vis[s] = true;
		dis[s] = depth;
		dfs(p[s], depth + 1);
	}
	nvis[s] = true;	/* 注意这里要在回溯的时候标记,不能在前面标记,
					   不然检测不到环。*/
}

/* 主函数在搜索前加一个判断 */
if (nvis[i])
{
	continue;
}

但是,尽管去掉了重复的点,提交之后还是会超时,这是因为每一次搜索的时候都要把vis和dis数组重新赋值为0,消耗了大量的时间。分析之后我们发现,vis和dis数组在搜索中虽然起局部变量的作用,但是每次搜索完之后这一部分由于nvis已经排除了重复的,所以本次搜索用到的部分在后面都不会有影响,即对于其他的搜索来说,可用的vis和dis都为0。因此,不需要每次搜索前都重新赋值。

完整的代码如下:

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 200005;
const int INF = 0x3F3F3F3F;

int n;			// 点数
int p[maxn];	// 传递对象编号
bool vis[maxn];	// 在一次搜索中访问过的点
int dis[maxn];	// 该点到本次搜索起点的距离
int ans = INF;	// 最小环长度

void dfs(int s, int depth)
{
	if (nvis[s])	//之前搜过的
	{
		return;
	}
	if (vis[s])	// 存在环
	{
		ans = min(ans, depth - dis[s]);	// 更新答案
	}
	else		// 不存在环就继续搜索
	{
		vis[s] = true;
		dis[s] = depth;
		dfs(p[s], depth + 1);
	}
	nvis[s] = true;	/* 注意这里要在回溯的时候标记,不能在前面标记,
					   不然检测不到环。*/
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &p[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		if (nvis[i])
		{
			continue;
		}
		// memset(vis, 0, sizeof(vis));
		// memset(dis, 0, sizeof(dis));
		dfs(i, 0);	// 从每个点开始搜索
	}
	printf("%d\n", ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值