您的位置:首页 > 其它

推箱子 Sokoban(华中农业比赛)

2016-05-19 21:11 225 查看
点这里 打开题目链接   点击打开链接

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

一顿暴力广搜;加状态标记。状态压缩需要用到一个类似于康拓的思想来压缩;所以容易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\n",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\n",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\n",bfs(man_pos,box,num));
}
return 0;
}


康拓的题目,详见  点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: