[NOIP2013 提高组] 华容道 P1979 洛谷

博客介绍了NOIP2013提高组的一道经典题目,涉及华容道问题的解决方法。通过结合SPFA和BFS的算法,并进行状态转化来优化求解过程。文章强调了如何处理多次询问,并对状态进行有效定义和优化,以降低空间复杂度。最后讨论了状态之间的联系和转移策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[NOIP2013 提高组] 华容道 P1979 洛谷

强烈推荐,更好的阅读体验

经典题目:spfa+bfs+转化

题目大意:

给出一个01网格图,和点坐标x,y空格坐标a,b,目标位置tx,ty要求移动空格最少步数使点到tx,ty

本题关键:

我们可以发现本题可以用BFS获得很高的暴力分,但是也可以使用DP:
f [ i ] [ j ] [ x ] [ y ] 表 示 空 格 在 i , j 目 标 点 在 x , y 的 最 少 操 作 次 数 f[i][j][x][y]表示空格在i,j目标点在x,y的最少操作次数 f[i][j][x][y]i,jx,y
但是本题的多次询问给我们一个启发–>可以预处理

所有我们可能可以预先处理一些状态的转移

可以发现很多状态是无效的,对于一个正确的移动路径:一定由两个部分组成

1.空格移动到目标格附近–>2.目标格借助空格移动到终点

对于前者很容易独立求出,对于后者,我们单独优化

目标点与空格的位置合并为一个状态,容易发现这个状态是4维的,空间卡住,时间__了

优化状态:
f [ i ] [ j ] [ 0 / 1 / 2 / 3 ] 表 示 目 标 点 x = i , y = j , 空 格 在 其 上 下 左 右 的 相 邻 位 置 的 状 态 f[i][j][0/1/2/3]表示目标点x=i,y=j,空格在其上下左右的相邻位置的状态 f[i][j][0/1/2/3]x=i,y=j,
为什么可以这样定义:因为在目标格借助空格移动到终点的过程中

假设目标点是下图黄球,空格只能是蓝球不能是绿球

在这里插入图片描述

不需要怎么了

状态之间的联系:

相邻状态:黄球位置确定下的所有蓝球位置(有效<=4)

所有对于一个状态考虑的转移左右3+1个

另外一个是空格目标交换位置(下图两种情况)

在这里插入图片描述

下面就可以上代码了

//先看主函数
#include<bits/stdc++.h>
#define ll int
#define f(i,a,b) for(ll i=a;i<=b;i++)
#define fd(i,a,b) for(ll i=a;i>=b;i--)
#define il inline
#define gc getchar()
#define r(i,a) for(ll i=fir[a];i;i=e[i].nex)
const ll maxn=32,INF=1e9,half=10,maxm=1e5;
ll n,m,q;
using namespace std;
bool Map[maxn][maxn];
ll xa[10]={-1,0,1, 0,0,0,0};
ll ya[10]={0, 1,0,-1,0,0,0};
         //下 左→  ←
ll f[maxn][maxn][half],cnt;
ll dis[maxn][maxn],fir[maxm];
//把所有数组定义提前,以免重复或re
struct edge{ll to,nex,w;}e[maxm<<1];

il void add(ll a,ll b,ll c){e[++cnt].to=b,e[cnt].nex=fir[a],e[cnt].w=c;fir[a]=cnt;}

//↑用于spfa的建边,在dfs中建边
ll getnum(ll x,ll y){return ((x-1)*(m)+y)<<2;}
//对于每个空格与目标个相邻的状态进行编号
ll fat(ll x){return (x+2)%4;}
//空格相对于目标格的位置下上右左-->上下左→
queue<pair<ll,ll> >que;
//记录空格在s的d方位
il void bfs(ll a,ll b,ll x,ll y,ll d){//重复使用bfs
	//a,b是枚举格子,x,y是空格
	memset(dis,-1,sizeof(dis));
	dis[a][b]=1;//防止被加入队列
	dis[x][y]=0;
	que.push(make_pair(x,y));
	while(!que.empty()){
		ll ux=que.front().first,uy=que.front().second;
		que.pop();
		f(i,0,3){
			ll vx=ux+xa[i],vy=uy+ya[i];
			if(Map[vx][vy]&&dis[vx][vy]==-1){
				que.push(make_pair(vx,vy));
				dis[vx][vy]=dis[ux][uy]+1;
			}
		}
	}
	if(d==5) return;//用于每次处理最少空格单独行走步数
	ll num=getnum(a,b);
	f(i,0,3){
		ll vx=a+xa[i],vy=b+ya[i];
		if(dis[vx][vy]>0)
			//状态连边
			add(num+d,num+i,dis[vx][vy]);
	}
	//交换位置,getnum表示相对位置取反
	add(num+d,getnum(x,y)+fat(d),1);
}
ll far[maxm];
bool vis[maxm];
queue<ll> Q;
il void spfa(ll sx,ll sy){//基本的spfa
	memset(far,-1,sizeof(far));//mem-1可以相当于赋值
	ll num=getnum(sx,sy);
	f(i,0,3){
		ll vx=sx+xa[i],vy=sy+ya[i];
		if(dis[vx][vy]!=-1){
			far[num+i]=dis[vx][vy];
			Q.push(num+i);
		}
	}
	//↑压入起始状态(<=4种)
	while(!Q.empty()){
		ll u=Q.front();
		Q.pop();
		vis[u]=0;
		r(i,u){
			ll v=e[i].to;
			if(far[v]>far[u]+e[i].w||far[v]==-1){
				far[v]=far[u]+e[i].w;
				if(!vis[v]){
					Q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}
int main()
{
	cin>>n>>m>>q;
	f(i,1,n) f(j,1,m) cin>>Map[i][j];
	f(i,1,n){
		f(j,1,m){
			if(!Map[i][j]) continue;
			f(o,0,3){
				//处理每相邻状态的空格移动的最小步数
				//包括目标点不动空格动(<=3种),目标空格交换位置(1种)
				ll x=i+xa[o],y=j+ya[o];
				if(Map[x][y]) bfs(i,j,x,y,o);
			}
		}
	}
	ll sx,sy,ex,ey,tx,ty,ans;
	while(q--){
		ans=INF;
		cin>>ex>>ey>>sx>>sy>>tx>>ty;
		if(sx==tx&&sy==ty){cout<<0<<endl;continue;}
		bfs(sx,sy,ex,ey,5);
		//借用bfs求出空格独立行走最短路
		spfa(sx,sy);
		ll num=getnum(tx,ty);
		f(i,0,3)
			if(far[num+i]!=-1) ans=min(ans,far[num+i]);
		cout<<((ans==INF)?-1:ans)<<endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值