【USACO17DEC】Push a Box——杨子曰题目

【USACO17DEC】Push a Box——杨子曰题目

题目描述
一个谷仓是一个N*M的矩形网格,有一些网格里有干草。Bessie站在其中一个格子内,还有一个格子里有一个大木箱。Bessie不能和大木箱在一个格子里,也不能和干草在一个格子里。

如果她不与干草一个格子,她就可以往自己旁边的四个方向(东西南北)移动,如果她想移动到有木箱的格子里,那个木箱就会被她推一格(只要木箱的那个方向还有空间),如果没有空间,那Bessie就不能移动了。

给你谷仓的布局(空格子,干草以及木箱位置)以及Bessie的出发位置和箱子要被推到的位置,请你帮忙计算Bessie能不能把木箱推到指定位置。

输入格式:

第一行有3个数,N,M,Q,其中N是谷仓的行数,M是列数。
1≤N,M≤1500.
1≤Q≤50,000.
接下来N行是谷仓的初始布局,其中“.”代表空格子,“#”代表干草格子,“A”代表Bessie的初始位置,“B”是木箱的初始位置。
接下来Q行,每行一个坐标(R,C),代表第R行第C行。对于每行,你要输出Bessie是否有可能把箱子推到这个位置。

输出格式:

Q行,每行一个答案,如果Bessie能走到,输出“YES”,否则输出“NO”。

输入样例:

5 5 4
##.##
##.##
A.B..
##.##
##.##
3 2
3 5
1 3
5 3

输出样例:

NO
YES
NO
NO

简直就是一道大毒瘤!这就是传说中的打代码5分钟 2小时,改代码2小时的大毒瘤


这道题的思路其实还是灰常简单滴!只不过小细节多了

首先,推箱子的过程显然是一个搜索,我们不妨使用BFS

那么我们来看一看某一个时刻的状态怎么表示,首先,肯定要有箱子的坐标(x,y),当然机智的你一定会想到我们还要记录一个人(←好像是奶牛)的位置,由于箱子在移动的时候,人一定贴着箱子,So,我们就可以用1,2,3,4来表示在箱子的上下左右,于是乎我们得到了状态:(x,y,i)

好滴,我们再来看一下状态如何转移,对于当前的状态,我们有两种选择:

  1. 继续往前推:(x,y,i)——>(x+dx[i],y+dy[i],i)
    注意!dx和dy数组表示箱子移动的4个方向,要预先设定好,通过(x-dx[i],y-dy[i])找到人的坐标,通过(x+dx[i],y+dy[i])找到箱子往前推所到的地方

  2. 走到箱子的另一面:(x,y,i)——>(x,y,i’)
    这里就是整道题的核心算法了,我们怎么判断当前状态下,我能不能从这个面走到另一个面,或者我再说的笼统一些,怎样判断当前状态下,两个点(也就是 (x-dx[i],y-dy[i]) 和 (x-dx[i’],y-dy[i’]) )是否联通
    咱们暴搜!——大哥,T了
    我们显然需要将这个信息预处理出来,但又由于箱子的位置在开始的时候我们不知道,不过有一点我们很清楚:箱子一定会堵掉一条从这个面到那个面的路径
    欧,也就是说两个点间只要有两条路径,就说明一定可以到达——对的,这就点双联通图(你可能不知道这是啥,其实就是没有割点(啥事割点?)的子图,这就保证,我删掉任意一个点,图都是联通的,求法和有向图中的强连通分量及其类似←,不过有一点要注意:同一个点可能会在多个点双连通图中

这样一来,问题轻松解决,简单描述一下流程:

  1. 跑一遍tarjan,求出所有的点双联通图
  2. 从人的初始位置开始灌一边水(↓为下面BFS的初始状态做准备↓)
  3. 从箱子周围刚才灌到水的地方开始BFS
  4. 对于每个询问x,y看一下(x,y,1),(x,y,2),(x,y,3),(x,y,4)中有没有状态是达到的

最终复杂度O(mn)

OK,完事


注:这道题我是开了O2才过的,如果有哪位大佬可以对我的算法或代码进行优化的话请在评论里回复,感谢

c++代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int maxn=1505;
const int dx[5]={0,1,0,-1,0},dy[5]={0,0,1,0,-1};

int n,m,q,bx,by,sx,sy,xx,yy;
int dfn[maxn*maxn],low[maxn*maxn],cnt=0,scc=0;
bool flag[maxn][maxn][5],vis[maxn][maxn],a[maxn][maxn];

vector<int> bl[maxn*maxn];

stack<int> s;

int id(int x,int y){
    return m*(x-1)+y;
}

void dfs(int x,int y,int fx,int fy){
    int v=id(x,y);
    dfn[v]=low[v]=++cnt;
    for (int i=1;i<=4;i++){
        int x0=x+dx[i],y0=y+dy[i],u=id(x0,y0);
        if (a[x0][y0]) continue;
        if (x0==fx && y0==fy) continue;
        if (!dfn[u]){
            s.push(u);
            dfs(x0,y0,x,y);
            low[v]=min(low[v],low[u]);
            if (low[u]>=dfn[v]){
                scc++;
                for (;;){
                    int k=s.top();
                    s.pop();
                    bl[k].push_back(scc);
                    if (k==u) break;
                }
                bl[v].push_back(scc);
            }
        }
        else if (x0!=fx || y0!=fy) low[v]=min(low[v],dfn[u]);
    }
}

void bfs1(){
    queue<int> qx,qy;
    qx.push(sx);
    qy.push(sy);
    vis[sx][sy]=1;
    while(!qx.empty()){
        int x0=qx.front(),y0=qy.front();
        for (int i=1;i<=4;i++){
            if (vis[x0+dx[i]][y0+dy[i]]) continue;
            if (a[x0+dx[i]][y0+dy[i]]) continue;
            if (x0+dx[i]==bx && y0+dy[i]==by) continue;
            qx.push(x0+dx[i]);
            qy.push(y0+dy[i]);
            vis[x0+dx[i]][y0+dy[i]]=1;
        }
        qx.pop(),qy.pop();
    } 
}


int check(int a,int b){
    for (int i=0;i<bl[a].size();i++){
        for (int j=0;j<bl[b].size();j++){
            if (bl[a][i]==bl[b][j]) return 1;
        }
    }
    return 0;
}

void bfs2(){
    queue<int> qx,qy,qd;
    for (int i=1;i<=4;i++){
        if (vis[bx-dx[i]][by-dy[i]]){
            qx.push(bx);
            qy.push(by);
            qd.push(i);
            flag[bx][by][i]=1;
        }
    }
    while(!qx.empty()){
        int x0=qx.front(),y0=qy.front(),d0=qd.front();
        
        if (!a[x0+dx[d0]][y0+dy[d0]]){
            qx.push(x0+dx[d0]);
            qy.push(y0+dy[d0]);
            qd.push(d0);
            flag[x0+dx[d0]][y0+dy[d0]][d0]=1;
        }
        for (int i=1;i<=4;i++){
            if (flag[x0][y0][i]) continue;
            if (i==d0) continue;
            if (a[x0-dx[i]][y0-dy[i]]) continue;
            if (check(id(x0-dx[i],y0-dy[i]),id(x0-dx[d0],y0-dy[d0]))){
                qx.push(x0);
                qy.push(y0);
                qd.push(i);
                flag[x0][y0][i]=1;
            }
        }
        qx.pop(),qy.pop(),qd.pop();
    }
}

int main(){
    scanf("%d%d%d\n",&n,&m,&q);
    for (int i=1;i<=n;i++){
        char ch;
        for (int j=1;j<=m;j++){
            scanf("%c",&ch);
            if (ch=='#') a[i][j]=1;
            else if (ch=='.') a[i][j]=0;
            else if (ch=='A') sx=i,sy=j,a[i][j]=0;
            else if (ch=='B') bx=i,by=j,a[i][j]=0;
        }
        scanf("%c",&ch);
    }
    for (int i=0;i<=n+1;i++){
        a[i][0]=a[i][m+1]=1;
    } 
    for (int i=0;i<=m+1;i++){
        a[0][i]=a[n+1][i]=1;
    }
    for (int i=1;i<=n;i++){
        for (int j=1;j<=m;j++){
            if (dfn[id(i,j)]==0 && a[i][j]==0){
                s.push(id(i,j));
                dfs(i,j,0,0);
            }
        }
    }
    bfs1();
    bfs2();
    while(q--){
        int qx,qy;
        scanf("%d%d",&qx,&qy);
        if (flag[qx][qy][1]||flag[qx][qy][2]||flag[qx][qy][3]||flag[qx][qy][4]||(qx==bx&&qy==by)) puts("YES");
        else puts("NO");
    }
    return 0;
}

于HG机房

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值