【USACO17DEC】Push a Box

题面

https://www.luogu.org/problem/P4082

题解

一道梵高级别的水题。但是因为是自己想出来的比较自豪,所以就写了。

$F[x][y][s]$代表石头在$(x,y)$,人在$(x+dx[s],y+dy[s])$,转移有两种,一种是推一步,一种是换个方向。

要判断“换个方向”时,是否可以走到那里,所以把点双求出来,把石头就看做割点,这里不需要建圆方树判断,因为如果它们不在一个点双里,说明只有唯一一条路(走石头),并且被堵上了,所以不能走过去,如果它们在一个点双里,说明可以到达。

判断两点是不是在一个点双里,我想了一下,发现没有特别好的方法,题解也写的暴力,那就暴力吧。

#include<stack>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ri register int
#define N 1550
using namespace std;

int n,m,q,vis[N][N],vis2[N][N][4],id[N][N];
int dfn[N*N],low[N*N],cnt,bccnt;
vector<int> bel[N*N];
char g[N][N];
stack<int> s;
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};

struct graph {
  vector<int> to[N*N];
  void add_edge(int u,int v) {to[u].push_back(v);}
  void tarjan(int x) {
    dfn[x]=low[x]=++cnt;
    s.push(x);
    for (ri i=0;i<to[x].size();i++) {
      int y=to[x][i];
      if (dfn[y]) {
        low[x]=min(low[x],dfn[y]);
      }
      else {
        tarjan(y);
        low[x]=min(low[x],low[y]);
        if (low[y]>=dfn[x]) {
          ++bccnt;
          while (1) {
            int t=s.top(); s.pop();
            bel[t].push_back(bccnt);
            if (t==y) break;
          }
          bel[x].push_back(bccnt);
        }
      }
    }
  }
} G;

struct node{int x,y;};
struct node2{int x,y,s;};

bool check(int ax,int ay,int bx,int by) {
  int a=id[ax][ay],b=id[bx][by];
  for (ri i=0;i<bel[a].size();i++)
    for (ri j=0;j<bel[b].size();j++) if (bel[a][i]==bel[b][j]) return 1;
  return 0;
}

void bfs2(int sx,int sy,int opt) {
  queue<node2> q;
  q.push((node2){sx,sy,opt}); vis2[sx][sy][opt]=1;
  while (!q.empty()) {
    int x=q.front().x,y=q.front().y,s=q.front().s;
    q.pop();
    for (ri i=0;i<4;i++) {
      if (i==s) continue;
      int nx=x+dx[i],ny=y+dy[i];
      if (!id[nx][ny] || vis2[x][y][i]) continue;
      if (check(nx,ny,x+dx[s],y+dy[s])) vis2[x][y][i]=1,q.push((node2){x,y,i});
    }
    int nx=x+dx[s^1],ny=y+dy[s^1];
    if (!id[nx][ny] || vis2[nx][ny][s]) continue;
    vis2[nx][ny][s]=1,q.push((node2){nx,ny,s});
  }
}

void bfs(int ax,int ay,int bx,int by) {
  queue<node> q;
  q.push((node){ax,ay}); vis[ax][ay]=1;
  while (!q.empty()) {
    int x=q.front().x,y=q.front().y;
    q.pop();
    for (ri i=0;i<4;i++) {
      int nx=x+dx[i],ny=y+dy[i];
      if (vis[nx][ny] || !id[nx][ny]) continue;
      vis[nx][ny]=1; q.push((node){nx,ny});
      if (nx==bx && ny==by) { bfs2(bx,by,i^1); return; }
    }
  }
}

int main(){
  scanf("%d %d %d",&n,&m,&q);
  memset(g,'#',sizeof(g));
  for (ri i=1;i<=n;i++) scanf("%s",g[i]+1),g[i][m+1]='#';

  int cc=0;
  for (ri i=1;i<=n;i++)
    for (ri j=1;j<=m;j++) if (g[i][j]!='#') id[i][j]=++cc;

  for (ri i=1;i<=n;i++) 
    for (ri j=1;j<=m;j++) if (id[i][j]) {
      for (ri k=0;k<4;k++) {
        int nx=i+dx[k],ny=j+dy[k];
        if (id[nx][ny]) G.add_edge(id[i][j],id[nx][ny]);
      }
    }

  for (ri i=1;i<=n;i++) 
    for (ri j=1;j<=m;j++) if (id[i][j] && !dfn[id[i][j]]) G.tarjan(id[i][j]);

  int ax,ay,bx,by;
  for (ri i=1;i<=n;i++) 
    for (ri j=1;j<=m;j++) if (g[i][j]=='A') ax=i,ay=j;
  for (ri i=1;i<=n;i++)
    for (ri j=1;j<=m;j++) if (g[i][j]=='B') bx=i,by=j;
  
  bfs(ax,ay,bx,by);

  int a,b;
  for (ri i=1;i<=q;i++) {
    scanf("%d %d",&a,&b);
    bool ans=(a==bx && b==by);
    for (ri j=0;j<4;j++) ans|=vis2[a][b][j];
    if (ans) puts("YES"); else puts("NO");
  }
  return 0;
}
原文地址:https://www.cnblogs.com/shxnb666/p/11420651.html