题目:
题解:
这种想着办法优化的题。。。
70pts:暴力——把空白格子随意乱移,遇到目标格子就换个位置,bfs就ok
100pts:bfs+spfa
很明确的是要想把格子S移到目标位置T,首先得把白格子E移到S的旁边,在通过一系列的操作使格子S到达T
格子S想要走到与它相邻的k方向(left,right,up,down)上的格子,必须先把E格子通过w的花费移到那里,然后在用1的花费使S走到那 里,总花费为w+1,由于从一个格子向相邻方向上走一步的花费要多次使用,因此我们把它预处理成数组move[x][y][k][h],表示这个棋子(x,y),白格子在k方向上,使棋子走到h方向上相邻的格子的最少花费,这个可以用BFS预处理
有了这么多东西,能不能用BFS做呢?不行。从一个点走到相邻的节点的花费不一定是1,而且还受白格子所在方向的影响,所以就不再设状态为(x,y),而是(x,y,k),k表示白格子的方向,dist[x][y][k]存储走到这个状态的最小步数。很明显,从一个格子向h方向走的最小代价就是move[x][y][k][h],很明显这个状态只能转移到(x,y,other(h)),other(h)表示和h相反的方向,比如你向右走完一次,白格子一定在这个格子的左侧。
现在已经构建了一个又状态(x,y,k)构成的一张图,边权也已经算出来了,因此想要从出发点找一条到结束点的最短路径,可以用SPFA
move[i][j][k][l]表示棋子在i,j时,白格子在ta的k方向上,使棋子走到l方向的最少花费
dis[i][j][k]表示棋子在i,j白格子在k方向的最小花费
代码:
70pts
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int EXi,EYi,SXi,SYi,TXi,TYi,n,m;
bool a[35][35],vis[35][35][35][35];
struct hh{int x,y,a,b,bs;};
const int c[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int bfs()
{
memset(vis,0,sizeof(vis));
queue<hh>q;
q.push((hh){EXi,EYi,SXi,SYi,0});
vis[EXi][EYi][SXi][SYi]=1;
while (!q.empty())
{
hh now=q.front(); q.pop();
if (now.a==TXi && now.b==TYi) return now.bs;
for (int i=0;i<=3;i++)
{
int xx=now.x+c[i][0],yy=now.y+c[i][1],aa=now.a,bb=now.b;
if (xx<1 || yy<1 || xx>n || yy>m || !a[xx][yy]) continue;
if (xx==now.a && yy==now.b){aa=now.x; bb=now.y;}
if (vis[xx][yy][aa][bb]) continue;
vis[xx][yy][aa][bb]=1;
q.push((hh){xx,yy,aa,bb,now.bs+1});
}
}
return -1;
}
int main()
{
int i,q,j;
scanf("%d%d%d",&n,&m,&q);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
scanf("%d",&a[i][j]);
while (q--)
{
scanf("%d%d%d%d%d%d",&EXi,&EYi,&SXi,&SYi,&TXi,&TYi);
if (SXi==TXi && SYi==TYi) {printf("0\n");continue;}
printf("%d\n",bfs());
}
}
100pts
#include <cstdio>
#include <queue>
#include <cstring>
#define up 1
#define down 2
#define left 3
#define right 4
#define INF 1e9
using namespace std;
struct point{int x,y;};
struct hh{int x,y,k;};
int EXi,EYi,SXi,SYi,TXi,TYi,n,m,deep[35][35],move[35][35][5][5],dis[35][35][5];
const int other[5]={0,2,1,4,3};
bool a[35][35],viv[35][35],vis[35][35][5];
point where(point x,int fx)
{
if (fx==up) x.x--;
if (fx==down) x.x++;
if (fx==left) x.y--;
if (fx==right) x.y++;
return x;
}
int bfs(point x,point y)
{
memset(deep,0x7f,sizeof(deep));
memset(viv,0,sizeof(viv));
queue<point>q;
q.push(x);
deep[x.x][x.y]=0;
viv[x.x][x.y]=1;
point now;
while (!q.empty())
{
now=q.front(); q.pop();
if (now.x==y.x && now.y==y.y) break;
for (int i=1;i<=4;i++)
{
point wo=where(now,i);
int xx=wo.x,yy=wo.y;
if (xx<1 || yy<1 || xx>n || yy>m || !a[xx][yy]) continue;
if (viv[xx][yy]) continue;
viv[xx][yy]=1;deep[xx][yy]=deep[now.x][now.y]+1;
q.push((point){xx,yy});
}
}
return deep[y.x][y.y];
}
void init()//move[i][j][k][l]表示棋子在i,j时,白格子在ta的k方向上,使棋子走到l方向的最少花费
{
int i,j,k,l;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
if (a[i][j])
{
a[i][j]=0;//这个目标格子的位置不能经过
for (k=1;k<=4;k++)
for (l=1;l<=4;l++)
{
if (l<k) {move[i][j][k][l]=move[i][j][l][k]; continue;}
point t1=where((point){i,j},k),t2=where((point){i,j},l);
if (!a[t1.x][t1.y] || !a[t2.x][t2.y]) continue;
move[i][j][k][l]=bfs(t1,t2)+1;
}
a[i][j]=1;
}
}
int spfa(point x,point y)//dis[i][j][k]表示棋子在i,j白格子在k方向的最小花费
{
memset(vis,0,sizeof(vis));
memset(dis,0x7f,sizeof(dis));
queue<hh>q;
a[y.x][y.y]=0;
for (int i=1;i<=4;i++)
{
int lj=bfs(x,where(y,i));
if (lj<INF) dis[y.x][y.y][i]=lj,q.push((hh){y.x,y.y,i}),vis[y.x][y.y][i]=1;
}
a[y.x][y.y]=1;
while (!q.empty())
{
hh now=q.front(); q.pop(); vis[now.x][now.y][now.k]=0;
for (int i=1;i<=4;i++)
{
point to=where((point){now.x,now.y},i);
if (to.x<1 || to.y<1 || to.x>n || to.y>m || !a[to.x][to.y]) continue;
if (dis[now.x][now.y][now.k]+move[now.x][now.y][now.k][i]<dis[to.x][to.y][other[i]])
{
dis[to.x][to.y][other[i]]=dis[now.x][now.y][now.k]+move[now.x][now.y][now.k][i];
if (!vis[to.x][to.y][other[i]]) vis[to.x][to.y][other[i]]=1,q.push((hh){to.x,to.y,other[i]});
}
}
}
int ans=INF+50;
for (int i=1;i<=4;i++) ans=min(ans,dis[TXi][TYi][i]);
if (ans>INF) return -1;
return ans;
}
int main()
{
int i,q,j;
scanf("%d%d%d",&n,&m,&q);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
scanf("%d",&a[i][j]);
init();
while (q--)
{
scanf("%d%d%d%d%d%d",&EXi,&EYi,&SXi,&SYi,&TXi,&TYi);
if (SXi==TXi && SYi==TYi) {printf("0\n");continue;}
printf("%d\n",spfa((point){EXi,EYi},(point){SXi,SYi}));
}
}
后方吐槽:这正解好烦啊啊啊啊啊啊

本文详细解析了一道关于游戏棋盘的问题,通过70分暴力解法和100分的SPFA+BFS优化策略,介绍了如何将空格移动到特定位置并交换棋子的方法。文章还分享了代码实现细节。
760

被折叠的 条评论
为什么被折叠?



