题目链接:点击打开链接
题意:
一只长度为L的蛇要到达左上角的出口,问最短步数。
思路:
在最基本的bfs里,我们要记录每个状态是否已经访问过,在基础题里通常只是一个点,设置一个vis[x][y]=true就ok了。
在这题中,我们要把蛇身整体在某一位置看成一个状态,要表达L长度的身体,需要的空间很大,将是400^8;
我们把蛇头单独抽取出来,作为vis数组的前两维。第三维,我们将蛇的身体压缩到一个数字中,蛇的身体的每一节,相对于前面一节的位置,只有4种可能,即前后左右4种可能,蛇身最长为7,那么我们用两个2进制位(或者说4进制)表示每一节身体相对前一节的位置,然后左移两位,之后重复操作整个蛇身。那么压缩后,占用的空间为4^7=16384。
要注意的一点是,头不允许到达刚才尾巴的位置,如第一图中B1不能马上到达B4的位置。
失误:
将bfs出口放在了循环里,导致遗漏情况。
PS:
Status | Accepted |
---|---|
Time | 4688ms |
Memory | 10944kB |
Length | 2424 |
Lang | G++ |
Submitted | 2017-03-10 14:51:52 |
Shared | |
RemoteRunId | 16704097 |
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
const int MAXN = 20+2;
int n, m, l;
bool tu[MAXN][MAXN];
bool vis[MAXN][MAXN][1<<14];
struct Tuple
{
int x, y;
};
struct Node
{
int step;
Tuple snake[8];
};
Node st;
inline bool judge(int x, int y, int z){
return (0<=x&&x<n && 0<=y&&y<m && !tu[x][y] && !vis[x][y][z]);
}
int getDirect(int x, int y, int tx, int ty){
if(x==tx){
if(y==ty-1) return 0; //up
else return 1; //down
}
else{
if(x-1==tx) return 2; //left
else return 3; //right
}
}
int cal(Node &t){
int ret = 0;
for(int i=1; i<l; ++i){
ret = (ret<<2) + getDirect(t.snake[i-1].x, t.snake[i-1].y, t.snake[i].x, t.snake[i].y);
}
return ret;
}
void SnakeMove(Node &now, Node &pre){
for(int i=1; i<l; ++i){
now.snake[i].x = pre.snake[i-1].x;
now.snake[i].y = pre.snake[i-1].y;
}
++now.step;
}
bool judgeBody(Node &now){
for(int i=1; i<l; ++i){
if(now.snake[i].x==now.snake[0].x && now.snake[i].y==now.snake[0].y)
return true;
}
return false;
}
int bfs(){
queue<Node> q;
st.step = 0;
q.push(st);
vis[st.snake[0].x][st.snake[0].y][cal(st)] = true;
while(!q.empty()){
Node temp = q.front(); q.pop();
for(int i=0; i<4; ++i){
Node now = temp;
now.snake[0].x += dx[i];
now.snake[0].y += dy[i];
int x = now.snake[0].x;
int y = now.snake[0].y;
if(x<0||y<0||x>=n||y>=m||tu[x][y]) continue; //判断蛇头是否在边界内以及是否为石头
if(x==now.snake[l-1].x&&y==now.snake[l-1].y) continue; //特殊情况不允许头追尾
SnakeMove(now, temp); //move and step+1
if(judgeBody(now)) continue; //判断蛇头是否和身体相撞
int z = cal(now);
if(vis[x][y][z]) continue; //判断是否访问过
if(x==0&&y==0) return now.step;
q.push(now);
vis[x][y][z] = true;
}
}
return -1;
}
int main(){
int x, y, cas=0;
while(scanf("%d%d%d", &n, &m, &l)!=EOF){
if(n==0&&m==0&&l==0) break;
memset(tu, 0, sizeof(tu));
memset(vis, 0, sizeof(vis));
for(int i=0; i<l; ++i){
scanf("%d%d", &x, &y);
st.snake[i].x = x-1;
st.snake[i].y = y-1;
}
int stone;
scanf("%d", &stone);
for(int i=0; i<stone; ++i){
scanf("%d%d", &x, &y);
tu[x-1][y-1] = true;
}
if(st.snake[0].x==0 && st.snake[0].y==0)
printf("Case %d: %d\n", ++cas, 0);
else
printf("Case %d: %d\n", ++cas, bfs());
}
return 0;
}