这几天把13年的提高做了,最后两道题参考了网上许多代码,最后终于改出来了,这里是day1最后一题。
描述
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
有些棋子是固定的,有些棋子则是可以移动的;
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EXiEXi 行第 EYiEYi 列,指定的可移动棋子的初始位置为第 SXiSXi 行第 SYiSYi 列,目标位置为第 TXiTXi 行第 TYiTYi 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。输入格式
第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;
接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。
接下来的 q 行,每行包含 6 个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。输出格式
输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。
样例
样例输入
3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2样例输出
2
-1样例说明
棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。
移动过程如下:
第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。
要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。数据范围
对于 30%的数据,1 ≤ n, m ≤ 10,q = 1;
对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10;
对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。来源
NOIP 2013 提高组 day 2
这个题就是搜索,问题就是怎么搜,一来写不来,直接写了个A*+hash+乱搞,写了很长,结果WA了85%。。。
然后听说直接bfs可以过60%,又加了个并查集优化,还是只过了60%。
然后。。。
就做不出来了
那么我们来一步一步分析。
首先,无论起始空白格和指定棋子在哪里,我们必须要将空白格移动到指定棋子四周。
然后,常规的搜索是移动空白格,这样会有810000种状态,一次dfs最坏就是8100000次,再dfs500次,肯定爆,我们就想,能不能直接移动指定棋子呢?
因每次移动指定棋子都需要空白在它四周,所以我们就将状态转换为300*4种,及用dis[i][j][k]表示指定棋子在(i,j),空白在它的k方向时的最小步数
然后就是怎么移动,比如我们要将下面的点右移一格可以这样做
先将空格移动到目标方向,再交换空格与目标棋子,因此我们可以建立个数组step[i][j][k][p],表示指定棋子在(i,j)要将空白从k方向移动到p方向需要的步数
找这个步数可以一来就先处理,只处理一遍,500个询问都可以用。
处理可以用bfs搜索,将(i,j)变为墙再bfs就可以了。
然后就是spfa,移动目标棋子,不断扩展就可以了,递推式如下:
if(dis[x][y][i^1]>dis[u.x][u.y][u.k]+step[u.x][u.y][u.k][i]+1)
{
dis[x][y][i^1]=dis[u.x][u.y][u.k]+step[u.x][u.y][u.k][i]+1;
if(!vis[x][y][i^1])
{
Q.push(note(x,y,i^1));
vis[x][y][i^1]=1;
}
}
有人会问,异或1是什么意思?
这里用异或1来表示反方向,因为移动后,空白将变换为目标节点的反方向,可以参考上面的图。
同时我们这样来设计增量数组
const int T[2][4]={{0,0,-1,1},
{-1,1,0,0}};
就可以保证异或1是反方向了。
ps:u是结构体,可在下面的全代码中查看。
这就是基本思路了
代码如下,不难,没有加注释,相信大家看得懂,看不懂也可以提出来。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 2147480000
#define M 31
using namespace std;
const int T[2][4]={{0,0,-1,1},
{-1,1,0,0}};
int step[M][M][4][4],dis[M][M][4];
int n,m,q,map[M][M],vis[M][M][4],f[M][M];
int ex,ey,sx,sy,tx,ty;
struct dot
{
int x,y;
dot(int _x=0,int _y=0):x(_x),y(_y){}
};
struct note
{
int x,y,k;
note(int _x=0,int _y=0,int _k=0):x(_x),y(_y),k(_k){}
};
int bfs(int x1,int y1,int x2,int y2)
{
if(!map[x1][y1]||!map[x2][y2])return INF;
if(x1==x2&&y1==y2)return 0;
queue<dot>Q;
while(!Q.empty())Q.pop();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=INF;
Q.push(dot(x1,y1));
f[x1][y1]=0;
while(!Q.empty())
{
dot u=Q.front();
Q.pop();
if(f[x2][y2]!=INF)return f[x2][y2];
for(int i=0;i<=3;i++)
{
int x=u.x+T[0][i],y=u.y+T[1][i];
if(x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]&&f[x][y]==INF)
{
f[x][y]=f[u.x][u.y]+1;
Q.push(dot(x,y));
}
}
}
return INF;
}
void get_step()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int t=map[i][j];
map[i][j]=0;
for(int k=0;k<=3;k++)
for(int p=0;p<=3;p++)
if(k==p)step[i][j][k][p]=0;
else step[i][j][k][p]=bfs(i+T[0][k],j+T[1][k],i+T[0][p],j+T[1][p]);
map[i][j]=t;
}
}
int spfa()
{
memset(vis,0,sizeof(vis));
queue<note>Q;
while(!Q.empty())Q.pop();
for(int i=0;i<=3;i++)
if(dis[sx][sy][i]!=INF)
{
Q.push(note(sx,sy,i));
vis[sx][sy][i]=1;
}
while(!Q.empty())
{
note u=Q.front();Q.pop();
vis[u.x][u.y][u.k]=0;
for(int i=0;i<=3;i++)
{
int x=u.x+T[0][i],y=u.y+T[1][i];
if(x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]&&step[u.x][u.y][u.k][i]!=INF)
if(dis[x][y][i^1]>dis[u.x][u.y][u.k]+step[u.x][u.y][u.k][i]+1)
{
dis[x][y][i^1]=dis[u.x][u.y][u.k]+step[u.x][u.y][u.k][i]+1;
if(!vis[x][y][i^1])
{
Q.push(note(x,y,i^1));
vis[x][y][i^1]=1;
}
}
}
}
int ans=INF;
for(int i=0;i<=3;i++)
ans=min(ans,dis[tx][ty][i]);
if(ans==INF)ans=-1;
return ans;
}
int work()
{
cin>>ex>>ey>>sx>>sy>>tx>>ty;
if(sx==tx&&sy==ty)return 0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<=3;k++)
dis[i][j][k]=INF;
map[sx][sy]=0;
for(int i=0;i<=3;i++)
dis[sx][sy][i]=bfs(ex,ey,sx+T[0][i],sy+T[1][i]);
map[sx][sy]=1;
return spfa();
}
int main()
{
#ifdef LOCAL
freopen("puzzle.in","r",stdin);
freopen("puzzle.out","w",stdout);
#else
#endif
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&map[i][j]);
get_step();
for(int i=1;i<=q;i++)
cout<<work()<<'\n';
return 0;
}
如果有什么问题,或错误,请在评论区提出,谢谢。