NOIP2013华容道

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_42557561/article/details/102732616
Analysis

拿到题后首选搜索
然后人家暴力都是七八十
我只有5分???
不知道哪里dfs挂了

至于一开始想到的bfs
因为害怕死循环而不知道怎么打标记所以弃了
翻了神仙的题解后,才意识到
bfs有个重要的性质,第一次访问到的节点到达它的距离一定最小
后面再到达就不优了,这个不仅可以用于剪枝,而且还可以避免重复走的情况
然后对于当前状态有用的就是现在在的位置和指定格子所在的位置,四维存一下即可(有点类似记忆化)

正解:
考虑q次询问中不变的是哪些。
由于棋盘是不会变化的,而指定格子能移动当且仅当空白格子在其上/下/左/右。那么我们就可以预处理出,每个格子作为指定格子时,空白格子在其四个方向上的状态
状态与状态之间建立关系
具体来说:
当前空格在指定格子下方,假定状态为 f [ x ] [ y ] [ 2 ] f[x][y][2]
现在要转移到空格在指定格子左方,即 f [ x ] [ y ] [ 3 ] f[x][y][3]
f [ x ] [ y ] [ 3 ] = f [ x ] [ y ] [ 2 ] + s t e p f[x][y][3]=f[x][y][2]+step
而所需要的最小步数就可以用BFS求出

这样将状态都处理出来后,对每个状态进行编号,建图
然后跑最短路
具体来说:
由于给定的空格位置和指定格子可能一开始并不相邻
我们需要先做一遍BFS将空格位置移到指定格子的四个方向(相当于有一个源点向这四个点连边,而最后的答案指定格子也可能在目标格子的四个方向,同理操作即可)

复杂度分析,,
不会,,

Code
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
}
const int  N=35;
int n,m,q;
int a[N][N];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int f[N][N];
const int M=4009;
int nxt[M<<4],head[M],to[M<<4],w[M<<4],ecnt=0;
int dis[M<<2];
inline void add(int x,int y,int z){
//	printf("u=%d v=%d val=%d\n",x,y,z);
	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;
}
inline int get_num(int x,int y){
	y--;
	return (x-1)*m+y<<2;
}
#define mp make_pair
void BFS(int ex,int ey,int px,int py,int d){
//	printf("px=%d py=%d ex=%d ey=%d\n",px,py,ex,ey);
	memset(f,-1,sizeof(f));
	f[ex][ey]=0;
	f[px][py]=1;
	queue<pair<int,int> > q;
	q.push(mp(ex,ey));
	while(!q.empty()){//预处理出来空格位置到其他位置的最短步数 
		int x=q.front().first,y=q.front().second;q.pop();
		for(re int i=0;i<4;++i){
			int xx=x+dx[i];
			int yy=y+dy[i];
			if(f[xx][yy]!=-1) continue;
			if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&a[xx][yy]){
				f[xx][yy]=f[x][y]+1;
				q.push(mp(xx,yy));
			}
		}
	}
	if(d==-1) return;
	int num=get_num(px,py);
//	printf("px=%d py=%d num=%d\n",px,py,num);
	for(re int i=0;i<4;++i){//状态转移 
		int x=px+dx[i];
		int y=py+dy[i];
//		x>=1&&x<=n&&y>=1&&y<=m&&
		if(f[x][y]>0)
			add(num+d,num+i,f[x][y]);
	}
	add(num+d,get_num(ex,ey)+(d+2)%4,1);
}
int INF;
bool vis[M<<2];
void spfa(int sx,int sy){
	queue<int> q;
	memset(vis,0,sizeof(vis));
	memset(dis,127/3,sizeof(dis));
	INF=dis[0];
	for(re int i=0;i<4;++i){
		int x=sx+dx[i];
		int y=sy+dy[i];
		if(x>=1&&x<=n&&y>=1&&y<=m&&f[x][y]!=-1){
			int num=get_num(sx,sy)+i;
			dis[num]=f[x][y];
			q.push(num);
//			printf("tmp=%d dis=%d\n",num,dis[num]);
		} 
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(re int e=head[u];e;e=nxt[e]){
			int v=to[e];
			if(dis[v]>dis[u]+w[e]){
				dis[v]=dis[u]+w[e];
				if(vis[v]) continue;
				vis[v]=1;
				q.push(v);
			}
		}
	}
}
int main(){
	n=in;m=in;q=in;
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=m;++j)
			a[i][j]=in;
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=m;++j)if(a[i][j]){
			if(a[i-1][j])BFS(i-1,j,i,j,0);
			if(a[i][j+1])BFS(i,j+1,i,j,1);
			if(a[i+1][j])BFS(i+1,j,i,j,2);
			if(a[i][j-1])BFS(i,j-1,i,j,3);
		}
	for(re int i=1;i<=q;++i){
		int ex=in,ey=in,sx=in,sy=in,tx=in,ty=in;
		if(sx==tx&&sy==ty){printf("0\n");continue;}
		BFS(ex,ey,sx,sy,-1);
		spfa(sx,sy);
		int ans=INF;
		for(re int j=0;j<4;++j)
			ans=min(ans,dis[get_num(tx,ty)+j]);
		if(ans>=INF) printf("-1\n");
		else printf("%d\n",ans);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/102732616
今日推荐