嘤嘤嘤泥古管理至今没给申题解
QAQ只有五天啦..
洛谷P1979 华容道 题解
距离NOIP2018还有7天qwq写篇题解积攒下人品
(第一次写紫题题解还有些小激动)
一些说明
1、本题解与代码中的0/1/2/3分别代表上/下/左/右
2、为了清晰表述,题解中记目标棋子(题目中的 \(SX,SY\) )为 \(B\) ,目标格子(题目中的 \(TX,TY\) 为 )\(C\),空白格为\(E\)
【思路分析】
如几位dalao所说,这道题的建图就是把可行的状态连边,对于每次询问跑最短路即可。
我用 $ ok[i][j][0/1/2/3] $表示 \(B\)位于点\((i,j)\)时,四个方向是否可能有空白格(先不考虑每组询问中空白格是否可以到达,只考虑该格子是否为固定格,或越界。)
此部分代码如下:
//判断哪些状态是合法的
for(int i=1;i<=n;i++)//空白格在棋子上下左右
for(int j=1;j<=m;j++){
if(!mapp[i][j])continue;//不可能有此状态
for(int k=0;k<4;k++)//四个方向
if(judge(i+xx[k],j+yy[k]))ok[i][j][k]=1;
}
judge函数用来判断是否为空白格或越界:
bool judge(int ax,int ay){
if(ax<=0||ax>n||ay<=0||ay>m)return 0;//边界
return mapp[ax][ay];
}
为了建图方便,我将状态存到一维数组里:
格子的编号:从上到下从左到右编
\(e.g.\)
- 1 2 3 4
- 5 6 7 8
- ……
状态的编号:\(e.g.\)
1号格子的四个状态编号为0123
于是得到如下编号函数:
int getnum(int ax,int ay,int t){
return ((ax-1)*m+ay)*4-(4-t);
}//t=0/1/2/3
由于\(q\)组询问的地图情况是相同的,我们可以先建图。
什么样的两个状态可以连边呢?
考虑两种情况:
·空白格子绕着\(B\)上下左右乱转
可以用bfs求出乱转最少多少次可以从上转到下,从左转到右...
需要注意的是,乱转时\(B\)是不能动的!!!(这个地方卡了好久QAQ)
·空白格子和\(B\)交换位置
必须保证两个状态都存在。
连上距离为1的边即可。
int bfs(int dx,int dy,int sx,int sy,int tx,int ty){//空白格子乱转的最小次数
//(sx,sy)出发到(tx,ty),不能经过(dx,dy)
queueq;
memset(vis,0,sizeof vis);
white st;st.x=sx;st.y=sy;
st.step=0;vis[sx][sy]=1;
q.push(st);
while(!q.empty()){
white noww=q.front();
q.pop();
if(noww.x==tx&&noww.y==ty)//到达目标格子
return noww.step;
for(int i=0;i<4;i++){//四个方向乱转
if(judge(noww.x+xx[i],noww.y+yy[i])){//如果合法
if(vis[noww.x+xx[i]][noww.y+yy[i]])continue;//正在访问
if(noww.x+xx[i]==dx&&noww.y+yy[i]==dy)continue;//不能碰到目标棋子
white nxt;
nxt.x=noww.x+xx[i];
nxt.y=noww.y+yy[i];
nxt.step=noww.step+1;
q.push(nxt);vis[noww.x+xx[i]][noww.y+yy[i]]=1;
}
}
}
return inf;//到不了
}
棋子不动,空白格乱转的情况 :
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<4;k++)
for(int l=k+1;l<4;l++){
if(ok[i][j][k]&&ok[i][j][l]){//必须都要合法
int aa=getnum(i,j,k);
int bb=getnum(i,j,l);
int cc=bfs(i,j,i+xx[k],j+yy[k],i+xx[l],j+yy[l]);
if(cc==inf)continue;
add(aa,bb,cc);add(bb,aa,cc);
}
}
qwq
for(int i=1;i<=n;i++)//空白与目标棋子左右互换
for(int j=1;j
这样我们的初始化就完成啦!
(加边的操作就和普通图论一样了w)
对于每一组询问,先特判一下:
while(q--){
scanf("%d%d%d%d%d%d",&ex,&ey,&bx,&by,&cx,&cy);
if(bx==cx&&by==cy){
puts("0");
continue;
}
if(!mapp[cx][cy]){
puts("-1");
continue;
}
if(!mapp[bx][by]){
puts("-1");
continue;
}
work();
}
然后考虑,怎样把一开始的状态转移到图上呢?
因为图上只存在空白格与\(B\) 相邻的状况,so暴力尝试让空白格跑到\(B\)的四个方向就好啦
for(int i=0;i<4;i++){//空白走到目标棋子旁边
if(judge(bx+xx[i],by+yy[i])){//这个点可以走
int nw=bfs(bx,by,ex,ey,bx+xx[i],by+yy[i]);
if(nw==inf)continue;//走不到
int nq=getnum(bx,by,i);
d[nq]=nw;
Q.push(nq);
viss[nq]=1;
}
}
然后,愉快地跑SPFA
while(!Q.empty()){
int noww=Q.front();Q.pop();viss[noww]=0;
for(int j=head[noww];j;j=b[j].nxt){
int vv=b[j].to;
if(d[vv]>d[noww]+b[j].dis){
d[vv]=d[noww]+b[j].dis;
if(!viss[vv])Q.push(vv),viss[vv]=1;
}
}
}
最后,愉快地检查是否能到\(C\)
int ans=inf;
for(int i=0;i<4;i++){
int qaq=getnum(cx,cy,i);
ans=min(ans,d[qaq]);
}
if(ans==inf)puts("-1");
else printf("%d\n",ans);
贴无注释代码QWQ
#include
using namespace std;
const int MAXN=35;
const int inf=99999999;
int n,m,q,ex,ey,bx,by,cx,cy;
bool mapp[MAXN][MAXN];
int xx[4]={-1,1,0,0};
int yy[4]={0,0,-1,1};
int getnum(int ax,int ay,int t){
return ((ax-1)*m+ay)*4-(4-t);
}
struct white{
int x,y;
int step;
};
bool judge(int ax,int ay){
if(ax<=0||ax>n||ay<=0||ay>m)return 0;
return mapp[ax][ay];
}
bool vis[MAXN][MAXN];
int bfs(int dx,int dy,int sx,int sy,int tx,int ty){
queueq;
memset(vis,0,sizeof vis);
white st;st.x=sx;st.y=sy;
st.step=0;vis[sx][sy]=1;
q.push(st);
while(!q.empty()){
white noww=q.front();
q.pop();
if(noww.x==tx&&noww.y==ty)
return noww.step;
for(int i=0;i<4;i++){
if(judge(noww.x+xx[i],noww.y+yy[i])){
if(vis[noww.x+xx[i]][noww.y+yy[i]])continue;
if(noww.x+xx[i]==dx&&noww.y+yy[i]==dy)continue;
white nxt;
nxt.x=noww.x+xx[i];
nxt.y=noww.y+yy[i];
nxt.step=noww.step+1;
q.push(nxt);vis[noww.x+xx[i]][noww.y+yy[i]]=1;
}
}
}
return inf;
}
struct edge{
int to,nxt,dis;
}b[5005];
int head[5005],tot;
void add(int u,int v,int w){
b[++tot].to=v;b[tot].nxt=head[u];
b[tot].dis=w;head[u]=tot;
}
bool ok[MAXN][MAXN][5];
void init(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(!mapp[i][j])continue;
for(int k=0;k<4;k++)
if(judge(i+xx[k],j+yy[k]))ok[i][j][k]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<4;k++)
for(int l=k+1;l<4;l++){
if(ok[i][j][k]&&ok[i][j][l]){
int aa=getnum(i,j,k);
int bb=getnum(i,j,l);
int cc=bfs(i,j,i+xx[k],j+yy[k],i+xx[l],j+yy[l]);
if(cc==inf)continue;
add(aa,bb,cc);add(bb,aa,cc);
}
}
for(int i=1;i<=n;i++)
for(int j=1;jQ;
bool viss[5005];
int d[5005];
void work(){
memset(d,128/3,sizeof d);
memset(viss,0,sizeof viss);
for(int i=0;i<4;i++){
if(judge(bx+xx[i],by+yy[i])){
int nw=bfs(bx,by,ex,ey,bx+xx[i],by+yy[i]);
if(nw==inf)continue;
int nq=getnum(bx,by,i);
d[nq]=nw;
Q.push(nq);
viss[nq]=1;
}
}
while(!Q.empty()){
int noww=Q.front();Q.pop();viss[noww]=0;
for(int j=head[noww];j;j=b[j].nxt){
int vv=b[j].to;
if(d[vv]>d[noww]+b[j].dis){
d[vv]=d[noww]+b[j].dis;
if(!viss[vv])Q.push(vv),viss[vv]=1;
}
}
}
int ans=inf;
for(int i=0;i<4;i++){
int qaq=getnum(cx,cy,i);
ans=min(ans,d[qaq]);
}
if(ans==inf)puts("-1");
else printf("%d\n",ans);
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)cin>>mapp[i][j];
init();
while(q--){
scanf("%d%d%d%d%d%d",&ex,&ey,&bx,&by,&cx,&cy);
if(bx==cx&&by==cy){
puts("0");
continue;
}
if(!mapp[cx][cy]){
puts("-1");
continue;
}
if(!mapp[bx][by]){
puts("-1");
continue;
}
work();
}
return 0;
}