Uva1601 The Morning after Halloween 双向bfs,建图

版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83758161

题目链接
16*16的网格上最多有3个小写字母,把它们移动到各自的目标位置处。每步可以移动多个字母(上下左右走一格或者不走),但字母不能占用同一位置,也不能在一步以内交换两个字母的位置。
输入保证所有空格连通,障碍连通,且任何2*2的子网格中至少有一个障碍格。输出最少的移动步数。保证有解。

本题收录于《编码的艺术》。

状态空间搜索(广义bfs)

已实现的优化点:

  1. 地图用一维存储,上下左右变成+16,-16,+1,-1。
  2. 把所有的空格预先抽成一张图,直接在图里bfs。
  3. 如果字母数量小于3,手动补足3,显著缩小代码量。
  4. 双向bfs。
  5. 使用快的图结构,如链式前向星。

未实现的优化点:

  1. 考虑到每个节点的度数不超过5,可以使用更快的图结构,如数组实现的邻接表。
  2. 状态保存时分多维保存,这样可以显著减少数组范围(256^3*2 -> 150^3*2),顺带减少memset的时间,但是会增加码量。
  3. 其它搜索,如A*
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std;
const int M = 300, MOD = 1000000007;
#define forcfs(uname,vname) for(int ev = fst[uname],vname=pnt[ev]; ev; ev=nxt[ev],vname=pnt[ev])

const int go[5]={0,-16,16,1,-1};

int fst[M], nxt[M*M], pnt[M*M], ecn;
inline void addEdge(int a,int b)
{
	pnt[++ecn] = b;
	nxt[ecn] = fst[a];
	fst[a] = ecn;
}

int mp[2][(1<<24)+16];
inline bool check(int ua,int ub,int va,int vb)
{
	return !((va==ub&&ua==vb) || va==vb);
}
int bfs(int s, int t)
{
	queue<int> q[2];
	memset(mp,-1,sizeof(mp));
	mp[0][s] = 0; q[0].push(s);
	mp[1][t] = 0; q[1].push(t);

	for(int step=0;q[0].size()||q[1].size();++step)
	for(int t=0;t<2;++t)
	while(q[t].size() && mp[t][q[t].front()]==step)
	{
		int u = q[t].front(); q[t].pop();
		int ua = u&0xff, ub = (u>>8)&0xff, uc = u>>16;
		forcfs(ua,va)
		forcfs(ub,vb) if(check(ua,ub,va,vb)) 
		forcfs(uc,vc) if(check(ua,uc,va,vc)&&check(ub,uc,vb,vc))
		{
			int v = va|(vb<<8)|(vc<<16);
			if(mp[t][v]==-1)
			{
				mp[t][v] = mp[t][u]+1;
				q[t].push(v);
				if(mp[t^1][v]!=-1) 
					return mp[t][v]+mp[t^1][v];
			}
		}
	}
	return -1;
}

int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
    #endif

	int row,col,n;
	while(scanf("%d%d%d",&col,&row,&n)&&row)
	{
		getchar();
		memset(fst,0,sizeof(fst));
		memset(nxt,0,sizeof(nxt));
		memset(pnt,0,sizeof(pnt));
		ecn = 0;

		char save[300]={};
		for(int i=0;i<row;++i)
			cin.getline(save+(i<<4),20);

		int maze[300]={};
		int s = 0, t = 0, cnt = 0;
		for(int i=0; i<M; ++i)
		{
			if(save[i]==' '||isalpha(save[i])) 
				maze[i] = ++cnt;
			if(isalpha(save[i]))
			{
				if(islower(save[i])) s |= maze[i]<<(8*(save[i]-'a'));
				else t |= maze[i]<<(8*(save[i]-'A'));
				save[i] = ' ';
			}
		}
		for(int i=0;i<M;++i) if(maze[i])
		{
			addEdge(maze[i],maze[i]);
			for(int k=1;k<=4;++k)
			{
				int j = i+go[k];
				if(j>=0 && maze[j])
				{
					addEdge(maze[i],maze[j]);
					addEdge(maze[j],maze[i]);
				}
			}
		}
		if(n<=2)
		{
			++cnt;
			addEdge(cnt,cnt);
			s |= cnt<<16;
			t |= cnt<<16;
		}
		if(n==1)
		{
			++cnt;
			addEdge(cnt,cnt);
			s |= cnt<<8;
			t |= cnt<<8;
		}
		printf("%d\n",bfs(s,t) );
	}

    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/83758161