『Pushbox 点双联通分量』
<更新提示>
<第一次更新>
<正文>
Pushbox
Description
周婧涵和她的小伙伴们发明了一个新游戏。游戏名字很准确,但不是特别有 创意。她们称之为“推动箱子在谷仓周围找到正确的位置,不要移动干草”游戏 (如果你认为这是浮夸的,你应该看到一些奶牛在编写代码时所使用的变量名 称…???)。
谷仓可以建模为一个 N×M 的矩形网格。一些网格单元中有干草。周婧涵在 这个网格中占据一个单元格,一个大木箱占据另一个单元格。周婧涵和木箱不能 同时放在同一个单元格中,也不能放入含有干草的单元格。
只要不走干草在的单元格,周婧涵可以在四个正交方向(北,东,南,西) 移动。如果她试图走到箱子所在的单元格,那么只要箱子的另一边有一个空的单 元格,箱子就会朝这个方向推动一个空间。如果没有空单元格,周婧涵将无法移 动。
一个特定的网格单元被指定为目标。周婧涵的目标是把箱子放到那个位置。
考虑到谷仓的布局、箱子和周婧涵的起始位置,以及箱子的目标位置,确定 是否有可能赢得比赛。
Input Format
第一行输入三个数字 N、M、Q,N 是网格的行数,M 是网格的列数。
1<=N,M<=1500 1<=Q<=50,000
接下来 N 行代表网格,内容由字符表示:空单元格(.),干草(#),周婧涵的起始位置(A)和木箱的初始位置(B)。
接下来是 Q 行,每行有一对整数(R,C)。 对于每一对,应该确定从谷仓 的初始状态开始是否有可能获得木箱的行 R,列 C。 起始行为 1,左起始列也是 1。
Output Format
Q 行,每行输出“YES”或者“NO”。
Sample Input
5 5 4
##.##
##.##
A.B..
##.##
##.##
3 2
3 5
1 3
5 3
Sample Output
NO
YES
NO
NO
解析
首先是\(bfs\)套\(bfs\)的暴力,第一重\(bfs\)箱子,第二重\(bfs\)人,时间复杂度\(O(n^2m^2)\),可以拿\(18\)分。
考虑优化,我们发现这道题只要判定合法性,于是第二重\(bfs\)肯定是我们优化的对象。
在暴力中,第二重\(bfs\)是用来找是否存在一条路径可以让人从箱子的一端不经过箱子走到另一端,这等价于判定箱子周围的两个点是否存在两条互不相同的路径可以互达(现在一条路径被箱子挡了,若另一条也存在就说明这次行动合法)。
这又等价于判定两个点是否在同一个点双联通分量中。
于是\(Tarjan\)预处理点双,由于一个点最多只有\(4\)条边,所以也最多属于\(4\)个点双,暴力判定即可,时间复杂度\(O(16^2nm)\),这样已经足以通过此题。
当然,我们可以把点双树或者圆方树建出来,判定两点树上距离是否小于等于\(1\)(圆方树上小于等于\(2\)),实现\(O(1)\)判定,时间复杂度\(O(16nm)\)。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1520;
struct edge { int ver,next; } e[N*N*8],ec[N*N*8];
struct node
{
int x,y;
node (int _x = 0,int _y = 0) { x = _x , y = _y; }
friend node operator + (node p1,node p2) { return node(p1.x+p2.x,p1.y+p2.y); }
friend node operator - (node p1,node p2) { return node(p1.x-p2.x,p1.y-p2.y); }
friend bool operator == (node p1,node p2) { return p1.x == p2.x && p1.y == p2.y; }
} man,box;
struct state
{
node p; int dir;
state (node _p = node(0,0),int _dir = 0) { p = _p , dir = _dir; }
};
const node d[] = {node(0,1),node(1,0),node(-1,0),node(0,-1)};
int n,m,s,t,Head[N*N],Map[N][N],v[N][N],f[N][N][4];
int dfn[N*N],low[N*N],st[N*N],fa[N*N*2],num,cnt,top;
node S[4]; int scnt;
vector <int> bel[N*N];
inline void input(void)
{
scanf("%d%d%d",&n,&m,&s);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
char c = ' ';
while ( isspace(c) ) c = getchar();
if ( c == '#' ) Map[i][j] = 0;
else Map[i][j] = 1;
if ( c == 'A' ) man = node(i,j);
if ( c == 'B' ) box = node(i,j);
}
}
inline void insert(int x,int y) { e[++t] = (edge){y,Head[x]} , Head[x] = t; }
inline bool valid(node p) { return p.x >= 1 && p.y >= 1 && p.x <= n && p.y <= m && Map[p.x][p.y]; }
inline int index(node p) { return ( p.x - 1 ) * m + p.y; }
inline void build(void)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int k=0;k<4;k++)
if ( valid(node(i,j)) && valid(node(i,j)+d[k]) )
insert( index(node(i,j)) , index(node(i,j)+d[k]) );
}
inline void Tarjan(int x)
{
dfn[x] = low[x] = ++num;
st[++top] = x;
for (int i=Head[x];i;i=e[i].next)
{
int y = e[i].ver;
if ( !dfn[y] )
{
Tarjan( y );
low[x] = min( low[x] , low[y] );
if ( low[y] >= dfn[x] )
{
int z; ++cnt;
do z = st[top--] , fa[z] = cnt;
while ( z != y );
fa[cnt] = x;
}
}
else low[x] = min( low[x] , dfn[y] );
}
}
inline void Prebfs(void)
{
queue <node> q;
q.push( man ) , v[man.x][man.y] = true;
while ( !q.empty() )
{
node x = q.front(); q.pop();
for (int i=0;i<4;i++)
{
node y = x + d[i];
if ( y == box ) S[++scnt] = x;
if ( valid(y) && !( y == box ) && !v[y.x][y.y] )
v[y.x][y.y] = true , q.push(y);
}
}
}
inline bool belong(node x,node y)
{
if ( !valid(x) || !valid(y) ) return false;
int ix = index(x) , iy = index(y);
return fa[fa[ix]] == iy || fa[fa[iy]] == ix || fa[ix] == fa[iy];
}
inline void Bfs(void)
{
queue <state> q;
for (int i=1;i<=scnt;i++)
for (int j=0;j<4;j++)
if ( S[i] + d[j] == box )
q.push(state(box,j)) , f[box.x][box.y][j] = true;
while ( !q.empty() )
{
state x = q.front(); q.pop();
for (int i=0;i<4;i++)
{
node y = x.p + d[i];
if ( valid(y) && belong(x.p-d[x.dir],x.p-d[i]) && !f[y.x][y.y][i] )
f[y.x][y.y][i] = true , q.push( state(y,i) );
}
}
}
inline void solve(void)
{
for (int i=1;i<=s;i++)
{
int x,y;
scanf("%d%d",&x,&y);
bool ans = f[x][y][0] || f[x][y][1] || f[x][y][2] || f[x][y][3] || box == node(x,y);
puts( ans ? "YES" : "NO" );
}
}
int main(void)
{
freopen("pushbox.in","r",stdin);
freopen("pushbox.out","w",stdout);
input();
build();
cnt = n * m;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if ( Map[i][j] && !dfn[index(node(i,j))] )
Tarjan( index(node(i,j)) );
Prebfs();
Bfs();
solve();
return 0;
}
<后记>
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步