POJ 1324 (A*搜索+状态压缩)

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;
}

猜你喜欢

转载自blog.csdn.net/tomandjake_/article/details/81533689