BFS+SPFA-NOIP2013 华容道

luoguP1979

思路挺难想的,一开始想着要以每个点预处理再SPFA,但是这样会很麻烦,而且会出问题

因为空格子在每个点的不同方向都是不一样的,所以我们考虑把状态抽象成点,用一个三维数组储存

id[i][j][k]表示空格在(i,j)的k方向

我们可以处理出状态之间的步数,可以用bfs做到

状态之间连好边,每次询问的时候设置一个起始状态和终止状态,将他们分别连向可转移的状态

具体看代码吧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define maxn 35
#define M 810005
#define inf 0x3f3f3f3f
using namespace std;
int n,m,t,a[maxn][maxn],cnt,head[M],dis[maxn][maxn][5][5];
int id[maxn][maxn][5],tot,d[maxn][maxn],ans[M];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
bool vis[M];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

struct EDGE{
	int to,nxt,w;
}edge[M<<1];

inline void add(int x,int y,int z){
	edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; edge[cnt].w=z;
}

inline bool judge(int x,int y){
	return (x>0 && x<=n && y>0 && y<=m);
}
queue< pair<int,int> > q;
inline int bfs(int x1,int y1,int x2,int y2){
	if(x1==x2 && y1==y2) return 0;//从x1,y1走到x2,y2的最少步数 
	while(!q.empty()) q.pop();
	memset(d,0,sizeof d);
	q.push(make_pair(x1,y1));
	while(!q.empty()){
		int x=q.front().first,y=q.front().second; q.pop();
		for(int i=0;i<4;i++){
			int nx=x+dx[i],ny=y+dy[i];
			if(!judge(nx,ny) || !a[nx][ny] || d[nx][ny]) continue;
			d[nx][ny]=d[x][y]+1;
			if(nx==x2 && ny==y2) return d[nx][ny];
			q.push(make_pair(nx,ny));
		}
	}
	return inf;
}

inline void prework(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<4;k++)
				id[i][j][k]=++tot;
	memset(dis,0x3f,sizeof dis);
	for(int i=1;i<=n;i++)//预处理(i,j),空格从其k方向移动到t方向需要的最少步数 
		for(int j=1;j<=m;j++)
			if(a[i][j]){
				a[i][j]=0;
				for(int k=0;k<4;k++){
					int x=i+dx[k],y=j+dy[k];
					if(!judge(x,y) || !a[x][y]) continue;
					for(int t=0;t<4;t++){
						int nx=i+dx[t],ny=j+dy[t];
						if(!judge(nx,ny) || !a[nx][ny]) continue;
						dis[i][j][k][t]=bfs(x,y,nx,ny)+1;
					}
				}
				a[i][j]=1;
			}//(i,j)往k方向移动一个格子的步数,状态取反 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<4;k++)
				for(int t=0;t<4;t++)
					if(dis[i][j][k][t]<inf)
						add(id[i][j][k],id[i+dx[t]][j+dy[t]][t^1],dis[i][j][k][t]);
	return; 
}
queue<int> Q;
inline int SPFA(int s,int t){
	memset(ans,0x3f,sizeof ans); memset(vis,0,sizeof vis);
	while(!Q.empty()) Q.pop();
	Q.push(s); vis[s]=1; ans[s]=0;
	while(!Q.empty()){
		int u=Q.front(); Q.pop();
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(ans[v]>ans[u]+edge[i].w){
				ans[v]=ans[u]+edge[i].w;
				if(!vis[v]) vis[v]=1,Q.push(v);
			}
		} vis[u]=0;
	}
	return ans[t]<inf?ans[t]:-1;
}


int main(){
	n=rd(); m=rd(); t=rd();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]=rd();
	prework();
	while(t--){
		int ex=rd(),ey=rd(),sx=rd(),sy=rd(),tx=rd(),ty=rd();
		if(!a[sx][sy] || !a[tx][ty] || !a[ex][ey]) {puts("-1");continue;}
		if(sx==tx && sy==ty) {puts("0");continue;}
		int S=++tot,T=++tot;
		a[sx][sy]=0;
		for(int i=0;i<4;i++){
			int x=sx+dx[i],y=sy+dy[i];
			if(a[x][y] && judge(x,y)){
				int tmp=bfs(ex,ey,x,y);//空格子走到起点周围 
				if(tmp<inf) add(S,id[sx][sy][i],tmp);
			}
		}
		a[sx][sy]=1;
		for(int i=0;i<4;i++){
			int x=tx+dx[i],y=ty+dy[i];
			if(a[x][y] && judge(x,y))
				add(id[tx][ty][i],T,0); 
		}
		printf("%d\n",SPFA(S,T));
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值