zoukankan      html  css  js  c++  java
  • 洛谷P1979 华容道 题解

    嘤嘤嘤泥古管理至今没给申题解
    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)
    	queue<white>q;
    	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<m;j++){
    		if(ok[i][j][3]&&ok[i][j+1][2]){ //注意谁左谁右
    			int aa=getnum(i,j,3);//别问我怎么知道的orz
    			int bb=getnum(i,j+1,2);
    			add(aa,bb,1);add(bb,aa,1);
    		}
    	}
    	for(int i=1;i<n;i++)//上下互换 
    	for(int j=1;j<=m;j++){
    		if(ok[i][j][1]&&ok[i+1][j][0]){
    			int aa=getnum(i,j,1);
    			int bb=getnum(i+1,j,0);
    			add(aa,bb,1);add(bb,aa,1);
    		}
    	}
    

    这样我们的初始化就完成啦!
    (加边的操作就和普通图论一样了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
    ",ans);
    

    贴无注释代码QWQ

    #include<bits/stdc++.h>
    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){
    	queue<white>q;
    	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;j<m;j++){
    		if(ok[i][j][3]&&ok[i][j+1][2]){ 
    			int aa=getnum(i,j,3);
    			int bb=getnum(i,j+1,2);
    			add(aa,bb,1);add(bb,aa,1);
    		}
    	}
    	for(int i=1;i<n;i++)
    	for(int j=1;j<=m;j++){
    		if(ok[i][j][1]&&ok[i+1][j][0]){
    			int aa=getnum(i,j,1);
    			int bb=getnum(i+1,j,0);
    			add(aa,bb,1);add(bb,aa,1);
    		}
    	}
    }
    queue<int>Q;
    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
    ",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;
    }
    
    

    完结撒花!!!

    _by Erutsiom _

  • 相关阅读:
    杨玲 201771010133《面向对象程序设计(java)》第六周学习总结
    杨玲 201771010133《面向对象程序设计(java)》第五周学习总结
    【Alpha】Scrum meeting 3
    【Alpha】Scrum meeting 2
    【Alpha】Scrum meeting 1
    实验八 团队作业四:团队项目需求分析建模与系统设计
    超越队 实验七 团队作业3 :团队项目需求分析与原型设计
    超越队 实验六 团队作业2 :校园失物招领系统
    超越队 实验五 团队作业1:软件研发团队组建与软件案例分析
    【Alpha】Scrum meeting 3
  • 原文地址:https://www.cnblogs.com/erutsiom/p/9904567.html
Copyright © 2011-2022 走看看