列表

详情


NC16536. [NOIP2013]华容道

描述

小 B最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间。
小 B玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
1.在一个 n x m 棋盘上有 n x m 个格子,其中有且只有一个格子是空白的,其余 n x (m-1) 个格子上每个格子上有一个棋子,每个棋子的大小都是 1 x 1 的;
2.有些棋子是固定的,有些棋子则是可以移动的;
3.任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。
游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的, 但是棋盘上空白的格子的初始位置、 指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候, 空白的格子在第 EXi 行第 EYi 列,指定的可移动棋子的初始位置为第 SXi 行第 SYi 列,目标位置为第 TXi 行第 TYi 列。
假设小 B每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

输入描述

第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n,m,q ;
接下来的 n 行描述一个 n x m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态, 0 表示该格子上的棋子是固定的, 1 表示该格子上的棋子可以移动或者该格子是空白的。 
接下来的 q 行,每行包含 6 个整数依次是 EXi,EYi,SXi,SYi,TXi,TYi ,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

输出描述

共 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出 −1 。

示例1

输入:

3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2

输出:

2
-1

说明:

棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
1.第一次游戏,空白格子的初始位置是 (3,2) (图中空白所示),游戏的目标是将初始位置在 (1, 2) 上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置 (2, 2) (图中红色的格子)上。

2.第二次游戏,空白格子的初始位置是 (1, 2) (图中空白所示),游戏的目标是将初始位置在 (2, 2) 上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2) 上

要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置 (2,2) 上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置, 游戏无法完成。

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(g++ 7.5.0) 解法, 执行用时: 22ms, 内存消耗: 536K, 提交时间: 2023-07-27 16:28:39

#include<bits/stdc++.h>
using namespace std;
const int N=35,M=4010;
int n,m,q,mp[N][N],dis[N][N],dist[M];
bool vis[M];
int e,to[M*5],nxt[M*5],val[M*5],hd[M];
int dx[5]={-1,1,0,0};
int dy[5]={0,0,-1,1};
void add(int x,int y,int w){ to[++e]=y; nxt[e]=hd[x]; val[e]=w; hd[x]=e; }
void bfs(int ei,int ej,int si,int sj,int dir){
    memset(dis,0,sizeof(dis));
    queue<pair<int,int> >q;
    q.push(make_pair(ei,ej)); dis[ei][ej]=1;
    while(!q.empty()){
        pair<int,int>w=q.front(); q.pop();
        int x=w.first,y=w.second;
        for(int i=0;i<4;i++){
            int xx=x+dx[i],yy=y+dy[i];
            if(!mp[xx][yy]||dis[xx][yy]||xx==si&&yy==sj) continue;
            dis[xx][yy]=dis[x][y]+1; q.push(make_pair(xx,yy));
        }
    }
    if(dir==4) return;

    for(int i=0;i<4;i++){
        int xx=si+dx[i],yy=sj+dy[i];
        if((xx==ei&&yy==ej)||!dis[xx][yy]) continue;
        add(si*30*4+sj*4+dir,si*30*4+sj*4+i,dis[xx][yy]-1);
    }
    add(si*30*4+sj*4+dir,ei*30*4+ej*4+(dir^1),1);
    return;
}
void spfa(int x,int y){
    memset(dist,0x3f,sizeof(dist));
    memset(vis,0,sizeof(vis));

    queue<int>q;
    for(int i=0;i<4;i++){
        if(!dis[x+dx[i]][y+dy[i]]) continue;
        int id=x*30*4+y*4+i; q.push(id);
        dist[id]=dis[x+dx[i]][y+dy[i]]-1; vis[id]=1;
    }
    while(!q.empty()){
        int id=q.front(); q.pop(); vis[id]=0;
        for(int i=hd[id];i;i=nxt[i]){
            int nxid=to[i],nxval=val[i];
            if(dist[nxid]>dist[id]+nxval){
                dist[nxid]=dist[id]+nxval;
                if(vis[nxid]) continue;
                vis[nxid]=1; q.push(nxid);
            }
        }
    }
    return;
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&mp[i][j]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(!mp[i][j]) continue;
            if(mp[i-1][j]) bfs(i-1,j,i,j,0);
            if(mp[i+1][j]) bfs(i+1,j,i,j,1);
            if(mp[i][j-1]) bfs(i,j-1,i,j,2);
            if(mp[i][j+1]) bfs(i,j+1,i,j,3);
        }
    }
    while(q--){
        int ex,ey,sx,sy,tx,ty;
        scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
        if(sx==tx&&sy==ty){ puts("0"); continue; } 

        bfs(ex,ey,sx,sy,4); spfa(sx,sy); int ans=0x3f3f3f3f;
        for(int i=0;i<4;i++) ans=min(ans,dist[tx*30*4+ty*4+i]);
        (ans==0x3f3f3f3f)?puts("-1"):printf("%d\n",ans);
    }
    return 0;
}

C++ 解法, 执行用时: 917ms, 内存消耗: 5508K, 提交时间: 2022-02-07 15:07:58

#include<bits/stdc++.h>
using namespace std;
int n,m,p;
int mapp[31][31];
bool mark[31][31][31][31];
int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};
struct data{
	int x,y;
	int kx,ky;
	int tim;
}a[810001];
bool judge(int x,int y,int kx,int ky)//判断能不能移动 
{
	if(kx<1||ky<1||kx>n||ky>m||mapp[kx][ky]==0) return 0;
	if(mark[x][y][kx][ky]) return 0;
	mark[x][y][kx][ky]=1;
	return 1;
}

void search()
{
	memset(mark,0,sizeof(mark));
	int t=0,w=0;
	int ex,ey;//目标位置 
	int x,y,kx,ky;//移动块,空白块 
	scanf("%d%d%d%d%d%d",&a[0].kx,&a[0].ky,&a[0].x,&a[0].y,&ex,&ey);
	if(ex==a[0].x&&ey==a[0].y){//如果移动块一开始就在目标位置 
		cout<<0<<endl;
		return;
	}
	mark[a[0].x][a[0].y][a[0].kx][a[0].ky]=1;
	while(t<=w){
		for(int i=0;i<4;i++){
			x=a[t].x;//移动块的位置 
			y=a[t].y;
			kx=a[t].kx+xx[i];//控制空白块四向移动 
			ky=a[t].ky+yy[i];
			if(x==kx&&y==ky){//如果空白块能移动到移动块,移动块到空白块的位置上 
				x=a[t].kx;
				y=a[t].ky;
			}
			if(judge(x,y,kx,ky)){//空白块不在移动块边上,但是状态合法,存储改图 
				w++;
				a[w].x=x;
				a[w].y=y;
				a[w].kx=kx;
				a[w].ky=ky;
				a[w].tim=a[t].tim+1;
				if(x==ex&&y==ey){
					cout<<a[w].tim<<endl;
					return;
				}
			}
		}
		t++;
	}
	cout<<-1<<endl;
	return;
}

int main()
{
	cin>>n>>m>>p;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d", &mapp[i][j]);
		}
	}
	for(int i=1;i<=p;i++){
		search();
	}
}

上一题