zoukankan      html  css  js  c++  java
  • 推箱子 Sokoban(华中农业比赛)

    点这里 打开题目链接   点击打开链接

     题目就是我们玩过的推箱子;


    一顿暴力广搜;加状态标记。状态压缩需要用到一个类似于康拓的思想来压缩;所以容易TLE,搜索就是用一个int型的数字来表示一个状态,

    压缩的和代码

      

    void set_C()
    {
        memset(C,0,sizeof(C));
        int i,j;
        for(i=0; i<=25; i++)
        {
            C[i][0]=1;
            for(j=1; j<=25; j++)
                C[i][j]=C[i-1][j-1]+C[i-1][j];// C数组是组合数,C n,m
        }
    }
    
    int binary_cantor(int num,int g)
    {
        int w,ans=0,y=g;
        for(int i=31; i>=0; i--)
        {
            if(num&(1<<i))
            {
                ans+=C[i][g];
                g--;
                if(g==0) break;
            }
        }
        return ans;
    }

    值得注意判断死角是相邻的两个方向不能够移动。

    剩下的在完整代码中说明。


    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <cmath>
    #include <set>
    #include <map>
    #include <list>
    #include <queue>
    #include <deque>
    #include <stack>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <stdlib.h>
    
    using namespace std;
    typedef long long LL;
    const int INF=2e9+1e8;
    const int MOD=1e9+7;
    const int MM=10;
    const int VIS=1081575*25+100;
    const int LEN=1081575+2;
    int n,m,dir[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};// 最好是让dir数组顺时针或者逆时针 方向转动
    string Push_Sokoban;// 记录地图中哪些地方有枪
    int C[30][30];//组合数
    int aim; // 用一个二进制位记录仓库的位置
    bool vis[VIS]; //标记状态的数组
    struct point
    {
        int x,y;
    };
    struct node
    {
        int pos,step,stauts;
    };
    void set_C()// 递推打印组合数;
    {
        memset(C,0,sizeof(C));
        int i,j;
        for(i=0; i<=25; i++)
        {
            C[i][0]=1;
            for(j=1; j<=25; j++)
                C[i][j]=C[i-1][j-1]+C[i-1][j];
        }
    }
    
    int binary_cantor(int num,int g)//压缩状态;  表示num这个数有g位二进制位为1 返回的数字表示num这个数在他们的排列中第几小
    {
        int w,ans=0,y=g;
        for(int i=31; i>=0; i--)
        {
            if(num&(1<<i))
            {
                ans+=C[i][g];
                g--;
                if(g==0) break;
            }
        }
        return ans;
    }
    bool is_out(int x,int y)// 是否越界
    {
        if(x>=0&&x<n&&y>=0&&y<m) return true;
        return false;
    }
    /***************************/
    //一维  与 二维 坐标的相互转化
    int change(int x,int y)
    {
        return x*m+y;
    }
    int change(point p)
    {
        return p.x*m+p.y;
    }
    point change(int x)
    {
        point p;
        p.x=x/m,p.y=x%m;
        return p;
    }
    /****************************/
    bool is_arrive(int box,int pos,int num)// 判断该状态是否来过
    {
        int temp=binary_cantor(box,num);
        int a=pos*LEN+temp;
        if(!vis[a])
        {
            vis[a]=1;// 未来过该状态则需要标记一下
            return true;
        }
        return false;
    }
    bool check_dead(int box,int bpos)// 是否进入死角,死角意味着, 该箱子未到仓库,且无法移动
    {
        point p=change(bpos);
        for(int i=0; i<4; i++)
        {
            if(!is_out(p.x+dir[i][0],p.y+dir[i][1])||Push_Sokoban[change(p.x+dir[i][0],p.y+dir[i][1])]=='*')
            {
                if(!is_out(p.x+dir[(i+1)%4][0],p.y+dir[(i+1)%4][1])||Push_Sokoban[change(p.x+dir[(i+1)%4][0],p.y+dir[(i+1)%4][1])]=='*')
                    if(!(aim&(1<<bpos))) return false;
            }
        }
        return true;
    }
    
    // 下面就是广搜核心了
    int bfs(int pos,int box,int number)// 全局变量注意清零
    {
        node first,second;
        first.step=0,first.pos=pos,first.stauts=box;
        queue<node>q;
        memset(vis,0,sizeof(vis));
        q.push(first);
        while(!q.empty())
        {
            first=q.front();
         //   cout<<bitset < sizeof(int)*8 > (first.stauts)<<endl;
         //   cout<<"@ "<<first.pos<<endl;
            if(aim==first.stauts) return first.step;
            q.pop();
            for(int i=0; i<4; i++)
            {
                point p=change(first.pos);
                int x=p.x+dir[i][0],y=p.y+dir[i][1];
                int bx=p.x+2*dir[i][0],by=p.y+2*dir[i][1];
                int box_pos=change(bx,by);
                box=first.stauts;
                pos=change(x,y);
                if(is_out(x,y)&&Push_Sokoban[pos]!='*')
                {
                    if((box&(1<<pos))&&is_out(bx,by)&&!(box&(1<<box_pos))&&Push_Sokoban[box_pos]!='*')// 该位置上有箱子
                    {
                        box=box&(~(1<<pos)),box=box|(1<<box_pos);
                        if(is_arrive(box,pos,number)&&check_dead(box,box_pos))   // 
                        {
                       //     printf("ren pos =%d x=%d y=%d
    ",pos,change(pos).x,change(pos).y);
                            second.pos=pos,second.stauts=box,second.step=first.step+1;
                            q.push(second);
                        }
                    }
                    else if(!(box&(1<<pos))&&is_arrive(box,pos,number))
                    {
                      //  printf("ren pos =%d x=%d y=%d
    ",pos,change(pos).x,change(pos).y);
                        second.pos=pos,second.stauts=box,second.step=first.step+1;
                        q.push(second);
                    }
                }
            }
        }
        return -1;
    }
    /**************************
    
    本题难点就在与状态压缩,如何给定一个数,给定二进
    制位1的个数,快速判断第几小,有点康拓的感觉
    *******************************************/
    int main(void)
    {
        int ncase;
        set_C();
        cin>>ncase;
        while(ncase--)
        {
            scanf("%d%d",&n,&m);
            Push_Sokoban.clear();
            int man_pos;
            int box=0,num=0;
            aim=0;
            for(int i=0; i<n; i++)
            {
                string s;
                cin>>s;
                for(int j=0; j<m; j++)
                {
                    if(s[j]=='m') man_pos=i*m+j,s[j]='.';
                    else if(s[j]=='b') s[j]='.',box=box|(1<<(i*m+j)),num++;
                    else if(s[j]=='w') s[j]='.',aim=aim|(1<<(i*m+j));
                    else if(s[j]=='+') s[j]='.',box=box|(1<<(i*m+j)),aim=aim|(1<<(i*m+j)),num++;
                }
                Push_Sokoban+=s;
            }
            //cout<<bitset < sizeof(int)*8 > (aim) <<endl;
            printf("%d
    ",bfs(man_pos,box,num));
        }
        return 0;
    }

    康拓的题目,详见  点击打开链接



  • 相关阅读:
    【Vegas原创】使用nid更改DBName和sid
    【Vegas2009】3月3日ubuntu上虚拟xp
    【Vegas原创】rebuild em (Database Control)【Windows/Linux版】
    【Vegas原创】ORA01113: file 1 needs media recovery解决
    【Vegas原创】LogMiner使用实践
    【Vegas原创】如何配置 SQL Server 2005 以允许远程连接
    Linux查看电脑硬件配置情况
    【Vegas原创】使用RMAN Duplicate进行本机对本机的复制
    【Vegas原创】修改oracle用户缺省主目录
    【Vegas原创】Oracle DB 截获造成CPU占用率很高的SQL(Windows)
  • 原文地址:https://www.cnblogs.com/coded-ream/p/7207977.html
Copyright © 2011-2022 走看看