Description:
n乘m平面内给出一个长度为L类似于贪吃蛇的生物helodox和k个障碍物,它的头为B1,每次可以朝向四个方向走,但是不能碰到自己的身体或者是障碍物,问它最少需要多少步才能到达出口(1,1)
Input:
n,m,L,L个初始身体位置点坐标,k,k个障碍物点坐标.
Output:
最少步数
Analysis:
这一题的难点在于状态的设计,若直接存储身体的每个位置坐标,内存太大,然而这样的话里面有一个隐含信息没有使用,那就是身体的每个点都是相连的,当我们考虑到这一信息是,会发现,身体的位置可能大大减小,可以考虑在状态里面记录头部的坐标r,c,然后使用一个bitset S,S中存入之后的每个点相对于前面一个点的方向,总共只有4个方向正好占二进制两位,所以S的二进制最大位数为(L-1)*2<=14,之后剩下的就是搜索啦。值得一提的是,先用bfs记录h的方式貌似比较常用,这样得到的估价函数也确实十分紧凑。
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<sstream>
#include<cmath>
#include<iterator>
#include<bitset>
#include<stdio.h>
#include<time.h>
using namespace std;
#define _for(i,a,b) for(int i=(a);i<(b);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long LL;
const int INF = 0xffffff0;
const int MOD = 1e9 + 7;
const int maxn = 25;
int n,m,L;
int h[maxn][maxn];
int pic[maxn][maxn];
struct State{
int r,c,S;
int g;
bool operator <(const State & rhs)const{
return g+h[r][c]>rhs.g+h[rhs.r][rhs.c];
}
State(int r,int c,int S,int g):r(r),c(c),S(S),g(g){}
State(){}
};
int dr[]={-1,1,0,0},dc[]={0,0,-1,1};
int vis[maxn][maxn];
bool ok(int r,int c){return r>=0&&r<n&&c>=0&&c<m&&!vis[r][c];}
void geth(){
memset(h,-1,sizeof(h));
memset(vis,0,sizeof(vis));
queue<pair<int,int> >Q;
Q.push({0,0});
vis[0][0]=1;h[0][0]=0;
while(Q.size()){
pair<int,int> now=Q.front();Q.pop();
for(int dir=0;dir<4;++dir){
int rr=now.first+dr[dir],cc=now.second+dc[dir];
if(ok(rr,cc)){
Q.push({rr,cc});
vis[rr][cc]=1;
h[rr][cc]=h[now.first][now.second]+1;
}
}
}
}
bool close[maxn][maxn][1<<14];
int Astar(const State & s0){
priority_queue<State> open;
memset(close,0,sizeof(close));
open.push(s0);
close[s0.r][s0.c][s0.S]=1;
while(open.size()){
State now=open.top();open.pop();
if(now.c==0&&now.r==0){
return now.g;
}
for(int dir=0;dir<4;++dir){
int rr=now.r+dr[dir],cc=now.c+dc[dir];
int S=0;
S|=dir;S<<=(L-1-1)*2;
S|=now.S>>2;
if(rr>=0&&rr<n&&cc>=0&&cc<m&&!close[rr][cc][S]&&!pic[rr][cc]&&h[rr][cc]!=-1){
int r=now.r,c=now.c;int S1=now.S;
bool valid=true;
for(int i=0;i<L-1;++i){
int dir=(S1>>((L-1-1-i)*2))&3;
r-=dr[dir],c-=dc[dir];
if(r==rr&&c==cc){
valid=false;
break;
}
}
if(valid){
open.push(State(rr,cc,S,now.g+1));
close[rr][cc][S]=1;
}
}
}
}
return -1;
}
int main() {
//freopen("C:\\Users\\admin\\Desktop\\in.txt", "r", stdin);
//freopen("C:\\Users\\admin\\Desktop\\out.txt", "w", stdout);
int kase=0;
while(scanf("%d%d%d",&n,&m,&L)&&(n||m||L)){
int r,c;int S=0;int r0,c0;
int lastr,lastc;
for(int i=0;i<L;++i){
scanf("%d%d",&r,&c);r--,c--;
if(i==0){r0=r,c0=c,lastr=r,lastc=c;}
else {
for(int dir=0;dir<4;++dir){
if(dr[dir]==lastr-r&&dc[dir]==lastc-c){
S|=dir;
if(i<L-1) S<<=2;
}
}
lastr=r,lastc=c;
}
}
State s0;
s0.r=r0,s0.c=c0,s0.g=0,s0.S=S;
memset(pic,0,sizeof(pic));
int k;scanf("%d",&k);
while(k--){
scanf("%d%d",&r,&c);r--,c--;
pic[r][c]=1;
}
geth();
int ans;
if(h[r0][c0]==-1) ans=-1;
else ans=Astar(s0);
printf("Case %d: %d\n",++kase,ans);
}
return 0;
}