寻宝

题目大意

迷宫可以描述为一个 n ∗ m n*m nm 的网格图,从上到下第 x x x 行,从左到右第 y y y 列的位置用 ( x , y ) (x,y) (x,y) 表示。

每个人可以在迷宫中移动移动若干次,每次可以向上下左右相邻的格子进行移动,但是无法移动到含有障碍物的格子。

除此以外,迷宫中还有 k k k 个传送门,每当一个人处在传送门入口所在的位置,就可以选择直接传送到出口所在的位置。

现在给出每个人的位置以及他们专属的宝藏的位置。

C 想知道,哪些人能够成功找到自己的宝藏。

能够成功找到自己的宝藏则输出 1 1 1,否则输出 0 0 0

对于 100 % 100\% 100% 的数据, n × m ≤ 50000 , k ≤ 100 , q ≤ 100000 n \times m \leq 50000,k \leq 100,q \leq 100000 n×m50000,k100,q100000

解题思路

首先明确,这题要注意传送门是单向的,即不能用并查集维护传送门。

先将二维表示成一维,即将 ( x , y ) (x,y) (x,y) 表示成 ( x − 1 ) ∗ m + y (x-1)*m+y (x1)m+y

然后用强连通缩点,再对传送门建单向边,跑 dfs。( k ≤ 100 k \leq 100 k100

没了。。。

CODE

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

int n, m, k, q;

char s[100007];

int mp[100007];

struct Grath
{
	int tot, head[100007], to[100007 << 1], nxt[100007 << 1];
	void add(int u, int v)
	{
		to[++tot] = v;
		nxt[tot] = head[u];
		head[u] = tot;
	}
} e1, e2;

int cnt_node, cntn, dfn[100007], low[100007], id[100007];

stack<int> ss;

void tarjan(int u)
{
	dfn[u] = low[u] = ++cnt_node;
	ss.push(u);
	for(int i = e1.head[u]; i; i = e1.nxt[i])
	{
		int v = e1.to[i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if(!id[v])
		{
			low[u] = min(low[u], dfn[v]);
		}
	}
	if(dfn[u] == low[u])
	{
		cntn++;
		while(1)
		{
			int now = ss.top();
			ss.pop();
			id[now] = cntn;
			if(now == u) break;
		}
	}
}

int xp[100007], yp[100007];

int flagg;

void dfs(int u, int fa, int en)
{
	if(flagg) return;
	if(u == en)
	{
		flagg = 1;
		return;
	}
	for(int i = e2.head[u]; i; i = e2.nxt[i])
	{
		int v = e2.to[i];
		if(v == fa) continue;
		dfs(v, u, en);
	}
}

signed main()
{
	scanf("%d%d%d%d", &n, &m, &k, &q);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%s", s);
		for(int j = 0, len = strlen(s); j < len; ++j)
			if(s[j] == '.') mp[(i - 1) * m + j + 1] = 1;
			else mp[(i - 1) * m + j + 1] = 0;
	}
	for(int i = 1; i <= n * m; ++i)
	{
		if(!mp[i]) continue;
		int now = i, lie = (i - (i / m) * m == 0 ? m : i - (i / m) * m), hang = i / m + (i % m == 0 ? 0 : 1);
		if(hang != 1 && mp[i - m])
			e1.add(now, i - m);
		if(hang != n && mp[i + m])
			e1.add(now, i + m);
		if(lie != 1 && mp[i - 1])
			e1.add(now, i - 1);
		if(lie != m && mp[i + 1])
			e1.add(now, i + 1);
	}
	for(int i = 1; i <= k; ++i)
	{
		xp[i] = (read() - 1) * m + read(), yp[i] = (read() - 1) * m + read();
		e1.add(xp[i], yp[i]);
	}
	for(int i = 1; i <= n * m; ++i)
		if(mp[i] && !dfn[i]) tarjan(i);
	for(int i = 1; i <= k; ++i)
		if(id[xp[i]] != id[yp[i]]) e2.add(id[xp[i]], id[yp[i]]);
	for(int i = 1; i <= q; ++i)
	{
		int p1 = (read() - 1) * m + read(), p2 = (read() - 1) * m + read();
		flagg = 0;
		dfs(id[p1], 0, id[p2]);
		if(flagg)
			puts("1");
		else
			puts("0");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值