USACO 2017 December Contest Platinum T2: Push a Box

Posted li-dox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USACO 2017 December Contest Platinum T2: Push a Box相关的知识,希望对你有一定的参考价值。

题目大意

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

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

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

题目分析

观察题目,发现并没有什么优秀的做法,所以只能考虑搜索。

然而单纯的搜索肯定是不行的,问题就是如何判断不经过箱子所在的点,从箱子的一个相邻点走到另一个相邻点。

也就是说,两个点就有两条点不相交的路径可以相互到达,这就等价于这两个点属于同一个点双连通分量。考虑使用圆方树判断一下。(一个点最多属于四个点双)

这样我们就可以顺利地bfs了。

更多细节看代码注释。

  1 #include<bits/stdc++.h>
  2 #define PII pair<int,int>
  3 #define MK make_pair
  4 #define fir first
  5 #define sec second
  6 
  7 using namespace std;
  8 const int MAXN=1510;
  9 
 10 struct Node
 11     int px,py,bx,by;
 12 ;
 13 int n,m,q;
 14 int px,py,bx,by;
 15 int dx[5]=-1,0,1,0;
 16 int dy[5]=0,-1,0,1;
 17 int PDCC,InCpr[MAXN][MAXN][5];// 点双个数,属于某个点的点双元素 
 18 
 19 int tim,dfn[MAXN][MAXN],low[MAXN][MAXN];
 20 char g[MAXN][MAXN];
 21 stack<PII > stk;
 22 
 23 bool Up,Dn,Lft,Rt,vis[MAXN][MAXN][5],B_vis[MAXN][MAXN];
 24 
 25 inline bool Inside(int x,int y)
 26     return (x>=1&&x<=n&&y>=1&&y<=m); 
 27 
 28 inline void AddNear(int x,int y,int Id)
 29     InCpr[x][y][++InCpr[x][y][0]]=Id;
 30 
 31 inline void Tarjan(int x,int y,int fx,int fy)
 32     dfn[x][y]=low[x][y]=++tim;
 33     stk.push(MK(x,y));
 34     for(int i=0,xx,yy;i<4;++i)
 35         xx=x+dx[i];yy=y+dy[i];
 36         if(Inside(xx,yy)&&g[xx][yy]!=#)
 37             if(!dfn[xx][yy])
 38                 Tarjan(xx,yy,x,y);
 39                 if(low[xx][yy]>=dfn[x][y])
 40                     ++PDCC;
 41                     AddNear(x,y,PDCC);
 42                     while(stk.top().fir!=xx||stk.top().sec!=yy)
 43                         AddNear(stk.top().fir,stk.top().sec,PDCC);
 44                         stk.pop();
 45                     
 46                     AddNear(xx,yy,PDCC);
 47                         stk.pop();
 48                 
 49                 low[x][y]=min(low[x][y],low[xx][yy]);
 50             
 51             else if(dfn[x][y]>dfn[xx][yy]&&(xx!=fx||yy!=fy))
 52                 low[x][y]=min(low[x][y],dfn[xx][yy]);
 53         
 54     
 55 
 56 inline void BeClose() //直接bfs就行 
 57     queue<PII > Q;
 58     Q.push(MK(px,py));
 59     B_vis[px][py]=true;
 60     while(!Q.empty())
 61         PII p=Q.front();
 62         Q.pop();
 63         if(p.fir==bx-1&&p.sec==by) Up=true;
 64         if(p.fir==bx+1&&p.sec==by) Dn=true;
 65         if(p.fir==bx&&p.sec==by-1) Lft=true;
 66         if(p.fir==bx&&p.sec==by+1) Rt=true;
 67         if(Up&&Dn&&Lft&&Rt) continue;
 68         for(int i=0,xx,yy;i<4;++i)
 69             xx=p.fir+dx[i];
 70             yy=p.sec+dy[i];
 71             if(Inside(xx,yy)&&g[xx][yy]!=#&&g[xx][yy]!=B&&!B_vis[xx][yy])
 72                 B_vis[xx][yy]=true;
 73                 Q.push(MK(xx,yy));
 74             
 75         
 76     
 77 
 78 inline bool InSameCpr(int s,int t,int p,int q) //判断在一个点双内 
 79     for(int i=1;i<=InCpr[s][t][0];++i)
 80         for(int j=1;j<=InCpr[p][q][0];++j)
 81             if(InCpr[s][t][i]==InCpr[p][q][j])
 82                 return true;
 83     return false;
 84 
 85 inline void Bfs()
 86     queue<Node> Q;
 87     if (Up) Q.push(Nodebx-1,by,bx,by),vis[bx][by][0]=true; 
 88     if (Lft)Q.push(Nodebx,by-1,bx,by),vis[bx][by][1]=true;
 89     if (Dn) Q.push(Nodebx+1,by,bx,by),vis[bx][by][2]=true;
 90     if (Rt) Q.push(Nodebx,by+1,bx,by),vis[bx][by][3]=true;
 91     while(!Q.empty())
 92         Node p=Q.front();Q.pop();
 93         for(int i=0;i<4;++i)
 94             int nbx=p.bx+dx[i],nby=p.by+dy[i];
 95             int bkx=p.bx+dx[i^2],bky=p.by+dy[i^2];
 96             if(Inside(nbx,nby)&&g[nbx][nby]!=#&&((bkx==p.px&&bky==p.py)||InSameCpr(p.px,p.py,bkx,bky))&&!vis[nbx][nby][i^2])
 97                 vis[nbx][nby][i^2]=1;
 98                 Q.push(Nodep.bx,p.by,nbx,nby);
 99             
100         
101      
102 
103 int main()
104     scanf("%d%d%d",&n,&m,&q);
105     for(int i=1;i<=n;++i)
106         for(int j=1;j<=m;++j)
107             char ch=getchar();
108             if(ch^.&&ch^#&&ch^A&&ch^B) //如果不是合法字符 
109                 --j;continue;
110             
111             if(ch==A) px=i,py=j;
112             if(ch==B) bx=i,by=j;
113             g[i][j]=ch;
114         
115 //    cout<<1<<endl;
116     Tarjan(px,py,0,0); //看看人所在点可以到达哪些点,并建出点双连通分量 
117 //    cout<<1<<endl;
118     if(!dfn[bx][by])  //如果一开始就到不了bx,by 
119         while(q--)
120             int x,y;
121             scanf("%d%d",&x,&y);
122             puts(x==bx&&y==by?"YES":"NO");
123         
124         return 0;
125     
126     BeClose(); //先让人接近箱子,看看能到箱子的哪些邻近位置,方便bfs 
127     Bfs();
128     while(q--)
129         int x,y;
130         scanf("%d%d",&x,&y);
131         puts(vis[x][y][0]||vis[x][y][1]||vis[x][y][2]||vis[x][y][3]?"YES":"NO");
132     
133     return 0;
134 

 

以上是关于USACO 2017 December Contest Platinum T2: Push a Box的主要内容,如果未能解决你的问题,请参考以下文章

USACO 2017 December Contest Platinum T2: Push a Box

USACO 2017 December Contest Platinum T3: Greedy Gift Takers

USACO 2017 December Contest Gold T1: A Pie for a Pie

USACO 2016 December Contest Gold T1: Moocast

USACO 2007 December Contest, Silver Problem 2. Building Roads Kruskal最小生成树算法

USACO 2018 December Contest Platinum T2: Sort It Out