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

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



  • 相关阅读:
    leetcode 190 Reverse Bits
    vs2010 单文档MFC 通过加载位图文件作为客户区背景
    leetcode 198 House Robber
    记忆化搜索(DP+DFS) URAL 1183 Brackets Sequence
    逆序数2 HDOJ 1394 Minimum Inversion Number
    矩阵连乘积 ZOJ 1276 Optimal Array Multiplication Sequence
    递推DP URAL 1586 Threeprime Numbers
    递推DP URAL 1167 Bicolored Horses
    递推DP URAL 1017 Staircases
    01背包 URAL 1073 Square Country
  • 原文地址:https://www.cnblogs.com/coded-ream/p/7207977.html
Copyright © 2011-2022 走看看