NOIP[2013] 华容道

      一道能够让人深刻理解“状态“含义的图论题目。首先一个很简单的做法就是暴力BFS,以空格子为起点进行BFS,需要记录的有2个量:空格子的位置和目标棋子的位置,我们考虑这样的复杂度,由于是BFS,每个节点只会被访问一次,不同的节点数有n*m*n*m个,再算上q组询问,复杂度为q*n*m*n*m。可以拿到60分。

      其实我们有很多状态都是没有必要的,题目中的q组询问中的地图是不会改变的,于是我们考虑先预处理再搞,我们可以知道只有当一个棋子四周存在空格子时,这个棋子才能移动,那么我们可以考虑一个状态F[i][j][k][h],表示从(i,j)这个位置,空格子在它的k方向,它要向h方向移动所需要的最少步数,这个我们可以提前用BFS求出,我们可以把格子(i,j)的K方向上是空格子当成一个状态,记作(i,j,k),那么对于一个F[i][j][k][h]来说,它就是连接(i,j,k)和(i+dx[h],j+dy[h],h的相反方向)的边的边权,这样我们先提前用BFS求出F[i][j][k][h],复杂度是O(n*m*4*4),然后对于每组询问,我们求出那个空格子到达起点格子四周的步数,然后连边,然后再跑一下spfa,复杂度大概是O(q*n*m*4*k)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1000005
int pre[maxn],last[maxn],other[maxn],len[maxn],dist[maxn];
int qx[maxn],qy[maxn],n,m,q,tot,cnt,que[maxn],l,S,T;
int f[31][31][5][5],g[31][31],vis[31][31],dis[32][32],id[31][31][5];
bool flag[maxn];
const int dx[5]={0,1,0,0,-1};
const int dy[5]={0,0,1,-1,0};

bool pd(int x,int y) 
{
	if (x<1||x>n||y<1||y>m) return 1;
	if (g[x][y]==0) return 1;
	return 0;	
}

void connect(int x,int y,int z)
{
	l++;
	pre[l]=last[x];
	last[x]=l;
	other[l]=y;
	len[l]=z;	
}

int bfs(int sx,int sy,int tx,int ty) 
{
	if (sx==tx&&sy==ty) return 0;
	int h=1,t=1;
	qx[1]=sx;qy[1]=sy;
	dis[sx][sy]=0;
	vis[sx][sy]=tot;
	while (h<=t) 
	{
		int x=qx[h],y=qy[h];h++;
		for (int i=1;i<=4;i++) 
		{
			int xx=x+dx[i];
			int yy=y+dy[i];
			if (pd(xx,yy)) continue;
			if (vis[xx][yy]!=tot) 
			{
				dis[xx][yy]=dis[x][y]+1;
				vis[xx][yy]=tot;
				t++;
				qx[t]=xx;qy[t]=yy;
				if (xx==tx&&yy==ty) return dis[xx][yy];
			}
		}
	}
	return 1e7;
}

void spfa(int s)
{
	memset(dist,53,sizeof dist);
	memset(flag,0,sizeof flag);
	dist[s]=0;
	que[1]=s;
	int h=1,t=1;
	while (h<=t) 
	{
		int u=que[h];h++;
		flag[u]=0;
		for (int p=last[u];p;p=pre[p]) 
		{
			int v=other[p];
			if (dist[v]>dist[u]+len[p]) 
			{
				dist[v]=dist[u]+len[p];
				if (!flag[v])	
				{
					que[++t]=v;
					flag[v]=1;	
				}
			}
		}
	}
}

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",&g[i][j]);
		
	for (int i=1;i<=n;i++) 
		for (int j=1;j<=m;j++) 
			for (int k=1;k<=4;k++) id[i][j][k]=++cnt;
		
	memset(f,53,sizeof f);
	
	for (int i=1;i<=n;i++) 
		for (int j=1;j<=m;j++) 
			if (g[i][j]) 
			{
				for (int k=1;k<=4;k++) 
				{
					if (pd(i+dx[k],j+dy[k])) continue;
					for (int h=1;h<=4;h++) 
					{
						if (pd(i+dx[h],j+dy[h])) continue;
						vis[i][j]=++tot;
						f[i][j][k][h]=bfs(i+dx[k],j+dy[k],i+dx[h],j+dy[h])+1;
						if (f[i][j][k][h]>1e7-1) continue;
				//		printf("%d %d %d %d %d\n",i,j,k,h,f[i][j][k][h]);
						connect(id[i][j][k],id[i+dx[h]][j+dy[h]][5-h],f[i][j][k][h]);
					}
				}
			}
			
	while (q--) 
	{
		int ex,ey,tx,ty,sx,sy;
		scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
		if (sx==tx&&sy==ty) 
		{
			printf("0\n");
			continue;	
		}
 		S=++cnt;T=++cnt;
		for (int i=1;i<=4;i++) 
		{
			vis[sx][sy]=++tot;
			if (pd(sx+dx[i],sy+dy[i])) continue;
			int temp=bfs(ex,ey,sx+dx[i],sy+dy[i]);
			if (temp>1e7-1) continue;
			connect(S,id[sx+dx[i]][sy+dy[i]][5-i],temp);	
		}	
		for (int i=1;i<=4;i++) 
		{
			if (pd(tx+dx[i],ty+dy[i])) continue;
			connect(id[tx][ty][i],T,0);
 		}
		spfa(S);
		if (dist[T]>1e7-1) printf("-1\n");
		else printf("%d\n",dist[T]+1);
	}
	return 0;	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值