POJ1324 状态压缩+BFS

很久之前就看到这道题了~~但是困于能力的问题~~~也没想到居然要状压~~~


蛇头每走一步,相邻的蛇身就会占领前一节蛇头/身所在的位置。因为蛇身只会只有4个方向可以前进,所以我们可以用0、1、2、3代表蛇身的相对前进方向。

这就需要两位二进制。。由于蛇身最多7节,2<<(2*7)=16384   继而20*20*16384是可以接受的状态数量~~


int ans[21][21][16400];///ans[i][j][k]:=i,j代表舌头所在地方~~k代表蛇身是怎么摆放的~~
///蛇身相对于前一块、要么向左0、要么向右1、要么向下2、要么向上3   所以每两位二进制代表当前蛇身的行进方向
///继而可以推出蛇的样子


操作顺序:

先初始化蛇身的前进方向。放进队列。

如果队列不为空、找出蛇头可以前进的方向、更新点并放入队列

这里一些优化,第一次找到(1,1)就可以break了~~

而且状态只需要遍历一次就可以了~~


#include <algorithm>
#include <iostream>
#include<string.h>
#include <fstream>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define exp 1e-8
#define fi first
#define se second
#define ll long long
#define INF 0x3f3f3f3f
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define all(a) a.begin(),a.end()
#define mm(a,b) memset(a,b,sizeof(a));
#define for0(a,b) for(int a=0;a<=b;a++)//0---(b-1)
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c
#define repp(a,b,c)for(int a=b;a>=c;a--)///
#define cnt_one(i) __builtin_popcount(i)
#define stl(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
using namespace std;
void bug(string m="here"){cout<<m<<endl;}
template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}
template<typename __ll>inline void read(__ll &m){READ(m);}
template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c,__ll &d){READ(m);READ(a);READ(b);READ(c);read(d);}
template < class T > inline  void out(T a){if(a>9)out(a/10);putchar(a%10+'0');}
template < class T > inline  void outln(T a){if(a>9)out(a/10);putchar(a%10+'0');puts("");}
template < class T > inline  void out(T a,T b){out(a);putchar(' ');out(b);}
template < class T > inline  void outln(T a,T b){out(a);putchar(' ');outln(b);}
template < class T > inline  void out(T a,T b,T c){out(a);putchar(' ');out(b);putchar(' ');out(c);}
template < class T > inline  void outln(T a,T b,T c){out(a);putchar(' ');outln(b);putchar(' ');outln(b);}
template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }
template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }
template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }
template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }
template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }

const int l=0;
const int r=1;
const int down=2;
const int up=3;
int ans[21][21][16400];///ans[i][j][k]:=i,j代表舌头所在地方~~k代表蛇身是怎么摆放的~~
///蛇身相对于前一块、要么向左0、要么向右1、要么向下2、要么向上3   所以每两位二进制代表当前蛇身的行进方向
///继而可以推出蛇的样子
bool vis[21][21][16400];
bool block[21][21];
int snake[10][3];
int dx[]={1,-1,0,0};///0下 1上 2右 3左
int dy[]={0,0,1,-1};
int OR[]={2,3,1,0};
int haha[]={0,0,3,15,63,255,1023,4095,16383};
int togo[4];
int n,m,L;
struct DAT
{
    int a,b,c;
    DAT(){}
    DAT(int _a,int _b,int _c)
    {
        a=_a;b=_b;c=_c;
    }
}cur;
void get_togo(int state)
{
    mm(togo,1);
    int x=0,y=0;
    int cnt=4;
    for(int i=1;i<L;i++)
    {
        switch(((state>>2*(i-1)))&3)
        {
        case down:
            x++;break;
        case up:
            x--;break;
        case l:
            y++;break;
        case r:
            y--;break;
        default:
            break;
        }
        if(y==0&&x==1)///不能往上走
            togo[1]=0,cnt--;
        if(y==0&&x==-1)
            togo[0]=0,cnt--;
        if(x==0&&y==1)
            togo[2]=0,cnt--;
        if(x==0&&y==-1)
            togo[3]=0,cnt--;
        if(cnt==0)break;
    }
}
int initstate()
{
    int state=0;
    int now=0;
    for(int i=2;i<=L;i++)
    {
        if(snake[i][1]==snake[i-1][1])///x相等~~
        {
            if(snake[i][2]+1==snake[i-1][2])
                state|=r<<(2*now);
            else if(snake[i][2]==snake[i-1][2]+1)
                state|=l<<(2*now);
        }
        else if(snake[i][2]==snake[i-1][2])
        {
            if(snake[i][1]+1==snake[i-1][1])
                state|=down<<(2*now);
            else if(snake[i][1]==snake[i-1][1]+1)
                state|=up<<(2*now);
        }
        now++;
    }
    return state;
}
int dfs()
{
    mm(vis,0);
    mm(ans,INF);
    queue<DAT>que;
    cur=DAT(snake[1][1],snake[1][2],initstate());
    if(snake[1][1]==1&&snake[1][2]==1)return 0;
    vis[cur.a][cur.b][cur.c]=1;
    ans[cur.a][cur.b][cur.c]=0;
    que.push(cur);
    while(!que.empty())
    {
        cur=que.front();
        que.pop();
        get_togo(cur.c);
        for(int dir=0;dir<4;dir++)
        {
            if(togo[dir]==0)continue;
            int x=cur.a+dx[dir];
            int y=cur.b+dy[dir];
            if(x>n||x<1||y>m||y<1)continue;
            if(block[x][y]==1)continue;
            int new_state=haha[L];
            new_state&=(cur.c<<2);
            new_state|=OR[dir];
            if(ans[x][y][new_state]>ans[cur.a][cur.b][cur.c]+1)
            {
                ans[x][y][new_state]=ans[cur.a][cur.b][cur.c]+1;
                if(x==1&&y==1)return ans[x][y][new_state];
                if(!vis[x][y][new_state])
                {
                    vis[x][y][new_state]=1;
                    que.push(DAT(x,y,new_state));
                }
            }
        }
    }
    return -1;
}
int main()
{
    int cas=1;
    while(scanf("%d%d %d",&n,&m,&L)!=EOF)
    {
        if(n+m+L==0)break;
        for1(i,L)read(snake[i][1],snake[i][2]);
        int tmp;read(tmp);
        mm(block,0);
        while(tmp--)
        {
            int a,b;
            read(a,b);
            block[a][b]=1;
        }
        int minn=dfs();
        printf("Case %d: %d\n",cas++,minn);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值