Online Judge:未知

Label:BFS,四进制状压,暴力,A*,哈希,玄学。

题目描述

给定一个n*m的地图和蛇的初始位置,地图中有些位置有石头,蛇不能经过。当然蛇也不能爬到地图之外。

每次移动,蛇头先动,接下来每节身体到达上一节身体所在的位置。蛇头将要去的地方,不能有身体的其他部分。

求蛇最少移动多少步到达(1,1)点。

下图B1是蛇头,B4是蛇尾,第二幅图是第一幅图蛇移动一步之后的效果,黑色区域是石头。

输入

第一行3个整数n、m、K,K表示蛇的长度。

接下来K行,每行两个整数,表示蛇每节身体的坐标,依次从蛇头到蛇蛇尾,坐标为行号和列号。

第K+2行一个整数s,表示石头的个数。 接下来s行,每行两个整数,表示一个石头的行号和列号。石头不会出现在(1,1)。

输出

输出蛇头最少多少步,可以到达(1,1)点。如果无法到达,输出-1。

样例

Input

4 4 1
3 3
2
2 2
2 3 5 5 2
3 3
3 2
4
2 2
2 3
3 4
4 2 5 6 4
4 1
4 2
3 2
3 1
3
2 3
3 3
3 4 4 4 4
2 3
1 3
1 4
2 4
4
2 1
2 2
3 4
4 2

Output

4

8

9

-1

Hint

对于30%的数据,K=1;

对于60%的数据,1≤K≤3,n和m的范围[2,10];

对于100%的数据,1≤K≤8,n和m的范围[2,20];

题解

由于n,m同阶,下面描述算法时间复杂度时统一用n。

30pts

就是普通的BFS。时间复杂度为\(O(N^2)\)。

#include<bits/stdc++.h>
using namespace std; const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1}; int n,m,k,mark[22][22];
struct node{int x,y;};
queue<node>q;
namespace p30{
int dis[22][22];
void bfs(int sx,int sy){
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)dis[i][j]=-1;
q.push((node){sx,sy});
dis[sx][sy]=0;
while(!q.empty()){
node now=q.front();q.pop();
int x=now.x,y=now.y;
if(x==1&&y==1)break;
for(int i=0;i<=3;i++){
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||ny<1||nx>n||ny>m||mark[nx][ny])continue;
if(dis[nx][ny]==-1){
dis[nx][ny]=dis[x][y]+1;
q.push((node){nx,ny});
}
}
}
printf("%d\n",dis[1][1]);
}
void solve(){
int sx,sy;scanf("%d%d",&sx,&sy);
for(int i=1;i<k;i++){
int x,y;scanf("%d%d",&x,&y);
}
int tmp;scanf("%d",&tmp);
for(int i=1;i<=tmp;i++){
int x,y;scanf("%d%d",&x,&y);
mark[x][y]=1;
}
bfs(sx,sy);
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
p30::solve();
}

60pts

由于身体最多只有三段。状态可以直接记录每段的位置\([x1][y1][x2][y2][x3][y3]\)。转移的话直接暴力把身子的坐标挪一下。

上面空间看起来比较悬,所以不要直接用\(dis[x1][y1][x2][y2][x3][y3]\)这个数组,在结构体里搞。

struct node{int x[3],y[3];};

由于广搜用到的状态应该不会太多,空间上不太可能会被卡掉。而时间上大致为\(O(N^k )=O(N^6)\)。

//如果把下面结构体里的x[],y[]开大,其实可以过掉这道题qwq。
#include<bits/stdc++.h>
using namespace std;
const int rx[]={-1,1,0,0},ry[]={0,0,-1,1};
int n,m,k,s;
bool mark[25][25];
struct node{
int x[3],y[3],step;
}A;
queue<node>Q;
bool check(int x,int y){
return x>=1&&x<=n&&y>=1&&y<=m&&!mark[x][y];
}
int bfs(){
Q.push(A);
node now,nxt;
while(!Q.empty()){
now=Q.front();
Q.pop();
if(now.x[0]==1&&now.y[0]==1)return now.step;
for(int i=0;i<4;i++){
bool flag=0;
int X=now.x[0]+rx[i],Y=now.y[0]+ry[i];
for(int j=0;j<k;j++)
if(X==now.x[j]&&Y==now.y[j])flag=1;
if(flag)continue;
if(check(X,Y)){
for(int j=1;j<k;j++){
nxt.x[j]=now.x[j-1];
nxt.y[j]=now.y[j-1];
}
nxt.step=now.step+1;
nxt.x[0]=X,nxt.y[0]=Y;
Q.push(nxt);
mark[X][Y]=1;
}
}
}
return -1;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<k;i++)scanf("%d%d",&A.x[i],&A.y[i]);
scanf("%d",&s);
for(int i=1;i<=s;i++){
int x,y;
scanf("%d%d",&x,&y);
mark[x][y]=1;
}
A.step=0;
mark[A.x[0]][A.y[0]]=1;
printf("%d\n",bfs());
return 0;
}

100pts

如果上面的结构体里数组开成k=8,再哈希一下或用A*之类的去优化,也可以过,而且比下面的解法还快。

记录每段身子的坐标太费空间了。

观察到身子是连着的(废话),所以可以像这样去记录,第二段在第一段的哪个方位。这样只有四个方向,可以用四进制状压

下面代码中:i在i+1左边:0,i在i+1上边:1,i在i+1右边:2,i在i+1下边:3。


BFS仍然是BFS,主要转移上会有点麻烦,注意细节。

代码如下:

/*
1
0[]2
3
*/
#include<bits/stdc++.h>
#define N 22
using namespace std;
const int dx[]={-1,1,0,0},dy[]={0,0,-1,1},Turn[]={3,1,2,0};
int n,m,k,mark[N][N];
int pw[10],dis[N][N][16500];
inline int gonxt(int S,int di){return S*4%pw[k-1]+Turn[di];}
//移动后,更新状态
inline bool boom(int x,int y,int S,int di){
//会撞到自己时返回1
int nx=x+dx[di],ny=y+dy[di];
for(register int i=1;i<k;++i){
int o=S-S/4*4;S/=4;
if(o==0)y--;if(o==1)x--;if(o==2)y++;if(o==3)x++;
if(x==nx&&y==ny)return 1;
}
return 0;
}
struct node{int x,y,S;}q[6600000]; inline int bfs(int sx,int sy,int fir){
memset(dis,-1,sizeof(dis));
int head=1,tail=0;
q[++tail]=((node){sx,sy,fir});dis[sx][sy][fir]=0;
while(head<=tail){
node now=q[head];head++;
int x=now.x,y=now.y,S=now.S;
if(x==1&&y==1)return dis[x][y][S];
for(register int i=0;i<=3;++i){
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||ny<1||nx>n||ny>m||mark[nx][ny]||boom(x,y,S,i))continue;
int T=gonxt(S,i);
if(dis[nx][ny][T]==-1){
dis[nx][ny][T]=dis[x][y][S]+1;
q[++tail]=(node){nx,ny,T};
}
}
}
return -1;
}
int main(){
register int sx,sy,x,y,lstx,lsty,S=0,tmp,i,v[9];
scanf("%d%d%d",&n,&m,&k);
pw[0]=1;
for(i=1;i<=9;++i)pw[i]=pw[i-1]*4;
scanf("%d%d",&sx,&sy);lstx=sx,lsty=sy; for(i=1;i<k;++i){
scanf("%d%d",&x,&y);
if(x!=lstx)v[i]=(x==lstx-1)?1:3;
else v[i]=(y==lsty-1)?0:2;
lstx=x,lsty=y;
}
for(i=k-1;i>=1;i--)S=S*4+v[i];
//初始状态(sx,sy,S)
scanf("%d",&tmp);
for(i=1;i<=tmp;++i)scanf("%d%d",&x,&y),mark[x][y]=1; cout<<bfs(sx,sy,S);
}

最新文章

  1. SQL Server日志文件(LDF文件)
  2. Ubuntu apt-get &quot;Hash Sum mismatch&quot; 问题解决方法
  3. 二:Go编程语言规范-类型
  4. Ubuntu 10.04 32位桌面版+OpnERP 6.1.1
  5. HTML CSS样式表布局
  6. 去除input在谷歌下的focus效果
  7. JavaScript中return的用法详解
  8. java并发包下的并发工具类
  9. slurm任务调度系统部署和测试(一)
  10. 查看系统分区df,查看、设置、修改、删除ACL权限
  11. Hibernate之Hibernate的体系结构
  12. 分布式进阶(七)Ubuntu下如何进入 Docker 容器
  13. QT5版本添加icon图标步骤
  14. python3 发送邮件
  15. Linux_CentOS-服务器搭建 &lt;七&gt;
  16. centos7管理用户权限
  17. Pycharm的常用快捷将
  18. JavaScript 函数入门略解
  19. 10 python os&amp;sys 模块
  20. java内存上堆和栈的一些理解

热门文章

  1. 剑指offer——25合并两个排序的链表
  2. Pathfinding 模板题 /// BFS oj21413
  3. myeclipe 中配置maven
  4. kafka 入门
  5. pointer &amp;&amp; reference
  6. 服务启动脚本start_boot.sh
  7. Twain协议部分翻译
  8. linux下svn 客户端使用方式
  9. opencv3.1.0 在控制台程序中报错:winnt.h(6464): error C2872: ACCESS_MASK: 不明确的
  10. [190308]Ubuntu 安装完之后,安装的软件小记