版权声明:欢迎评论交流,转载请注明原作者。 https://blog.csdn.net/m0_37809890/article/details/83758161
题目链接
16*16的网格上最多有3个小写字母,把它们移动到各自的目标位置处。每步可以移动多个字母(上下左右走一格或者不走),但字母不能占用同一位置,也不能在一步以内交换两个字母的位置。
输入保证所有空格连通,障碍连通,且任何2*2的子网格中至少有一个障碍格。输出最少的移动步数。保证有解。
本题收录于《编码的艺术》。
状态空间搜索(广义bfs)
已实现的优化点:
- 地图用一维存储,上下左右变成+16,-16,+1,-1。
- 把所有的空格预先抽成一张图,直接在图里bfs。
- 如果字母数量小于3,手动补足3,显著缩小代码量。
- 双向bfs。
- 使用快的图结构,如链式前向星。
未实现的优化点:
- 考虑到每个节点的度数不超过5,可以使用更快的图结构,如数组实现的邻接表。
- 状态保存时分多维保存,这样可以显著减少数组范围(256^3*2 -> 150^3*2),顺带减少memset的时间,但是会增加码量。
- 其它搜索,如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;
}