noip2013 华容道(bfs + spfa)

5 篇文章 0 订阅
2 篇文章 0 订阅

//拖了两周,终于把这道题A了

//没事千万不要在bfs和spfa里memset(打板子打习惯了memset),我就t了好久,泪流满面。还有一定要把哪个是目标,那个是起点的坐标看清楚啊,我调了1个小时才看出来。。。蒟蒻的我不想说什么了。。。。

这道题求每次游戏所需要的最少时间,可以考虑建图跑spfa求最短路。

怎么建?//不建让空格到处乱跑,bfs到指定初始棋子移到了目标位置。就停止,这样做肯定超时可能只有30吧。

//建的话可以把指定初始棋子的位置,和空格的位置绑在一起,空格可以向四个方向走,每个状态可以向4个状态连边。一共nm^2个状态。复杂度qnm^2,显然超时。

其实只有空格在指定初始棋子所在的位置周围(4个方向)才有用。

num[i][j][k]:表示空格在格子(i,j)的k方向上,这个状态的编号。(k = 1,左方向;2右方向;3下方向;4上方向)

首先空格和指定初始棋子可以交换位置,那么我们对于每个为1的格子(1 表示该格子上的棋子可以移动或者该格子是空白的。),向它四周为1的格子边权为1的连边 。

假设向它上连边,表示空格在格子(i,j)上的状态到,空格在格子(i-1,j)下面的状态所需要的步数为1.

其次空格可以从指定初始棋子所在位置周围的一个方向,跳到另一个方向。把这两种边权连边,边权可以bfs出来。

最后有可能空格最开始不在指定初始棋子所在位置周围,我们用bfs求出空格到指定初始棋子所在位置周围的每个方向所需要的最小步数,在把这写最小步数,拿来跑多起点spfa.最后答案就是空格到达在目标位置周围所需要的最小步数。

#include<bits/stdc++.h>
using namespace std;
int tot, num[34][34][4], dis[7000], qx[4000000], qy[4000000], qt[4000000], ma[35][35], n, m, qq,q[4000000],vis[35][35],vis1[7000];
int zl[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; 
int nex[70000], tp, h[70000], tov[70000], tow[70000];
void read(int &x)
{
	x = 0; int f = 0; char c = getchar();
	while(c < '0' || c > '9')
    {
    	if(c == '-') f = 1; c = getchar();
    }
    while(c >= '0' &&  c <= '9')
    {
    	x = x *10  + c -'0'; c = getchar();
    }
    if(f) x = -x;
}
void add(int x,int y,int w)
{
	tp++;
	tov[tp] = y;   tow[tp] = w;  nex[tp] = h[x];  h[x] = tp;
}
int bfs(int ex,int ey,int zx,int zy,int bx,int by)
{
	if(ex == zx && ey == zy) return 0;
	for(int i = 1; i <= n; i++)
	 for(int j = 1; j <= m ;j ++)
	 vis[i][j] = 0;
	 int head = 0, tail = 1;
	 qx[tail] = ex;  qy[tail] = ey;  vis[ex][ey] = 1;
	 while(head < tail)
	 {
	 	head++;
	 	int x = qx[head]; int y = qy[head];
		 for(int i = 0; i <= 3; i++)
		 {
		 	int xx = x + zl[i][0]; int yy = y + zl[i][1];
		 	if(ma[xx][yy] && vis[xx][yy] == 0 && xx >= 1 && xx <= n && yy >= 1 && yy <= m && (xx != bx ||   yy != by))
		 	{
		 		if( xx == zx && yy == zy ) return qt[head] + 1;
		 		vis[xx][yy] = 1;     tail++;
		 		qx[tail] = xx; qy[tail] = yy; qt[tail] = qt[head] + 1;
			 }
		  } 
	  } 
	  return 1e9;
}
int spfa(int ex,int ey,int sx,int sy,int tx,int ty)
{
	if(sx == tx && sy == ty) return 0;
	for(int i = 1; i <= tot; i++)
	dis[i] = 1e9;
	memset(vis1,0,sizeof(vis1));  
	int head = 0, tail = 0;
	for(int k = 0; k <= 3; k++)
	{
		int x = sx + zl[k][0]; int y = sy + zl[k][1];
		if(!num[sx][sy][k]) continue; 
		dis[num[sx][sy][k]] = bfs(ex,ey,x,y,sx,sy);
		q[++tail] = num[sx][sy][k];
	    vis1[num[sx][sy][k]] = 1;
	}
	while(head < tail)
	{
		int x = q[++head]; 
		for(int i = h[x]; i; i = nex[i])
        {
        	int v = tov[i];
        	if(dis[v] > dis[x] + tow[i] )
        	{ 
			 dis[v] = dis[x] + tow[i];
		 if(vis1[v] == 0) { vis1[v] = 1; tail++; q[tail] = v; }
			}	
 	    }
       vis1[x] = 0;
     }
     int ans = 1e9;
     for(int k =  0; k <= 3; k++)
    if(num[tx][ty][k])   ans = min(ans,dis[num[tx][ty][k]]);
	  return ans == 1e9?-1:ans; 
}
int main()
{
	read(n);read(m);read(qq);
	for(int i = 1; i <= n; i++)
	 for(int j = 1; j <= m; j++)
	   	 read(ma[i][j]);
	for(int i = 1; i <= n; i++)
      for(int j = 1; j <= m; j++)
      if(ma[i][j]) 
	   for(int k = 0; k <= 3; k++)
        {
        	int xx = i + zl[k][0]; int yy = j + zl[k][1];	
			if(ma[xx][yy] == 0) continue;
        	if(num[i][j][k] == 0) num[i][j][k] = ++ tot;
        	if(num[xx][yy][k^1] == 0) num[xx][yy][k^1] = ++ tot;
            add(num[i][j][k],num[xx][yy][k^1],1);
		}
	for(int i = 1; i <= n; i++)
	 for(int j = 1; j <= m; j++)
	  for(int k = 0; k <= 3; k++)
	   for(int kk = k+1; kk <= 3; kk ++)
	    {
	    int xx = i + zl[k][0]; int yy = j + zl[k][1];	
		int x1 = i + zl[kk][0]; int y1 = j + zl[kk][1];		
	    if(k != kk && num[i][j][k] && num[i][j][kk])
	    {         int haha = bfs(xx,yy,x1,y1,i,j);
		add(num[i][j][k],num[i][j][kk],haha);	 add(num[i][j][kk],num[i][j][k],haha);} 
		}
	 for(int i = 1; i <= qq; i++)
	 {
	 	int ex, ey, sx, sy, tx, ty;
	 	read(ex);read(ey);read(sx);read(sy);read(tx); read(ty);
	 	printf("%d\n",spfa(ex,ey,sx,sy,tx,ty));
	 }
	 return 0;
 }
  

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值