题意:给定一个n*m的国际象棋棋盘。共有5种字符,'.'表示空,'*'表示白色王,‘K’表示黑色马,‘B’表示黑色象,‘R’表示黑色车。白色只有唯一一个棋子——王,黑色最多不超过15。现在黑色防出现问题,不能走步,但会吃掉出现在棋子路线上的白色棋子。问最少多少步,白色王可以把黑色全部吃完。若吃不完就输出-1.
题解:将黑色棋子保存到数组中,记录为0~t,令i为黑色棋子状态。i转换成二进制后,第k位为0表示第k个棋子已经被吃掉,为1表示未被吃掉,所以0<=i<=(1<<t)。从而map[i][j][k]表示在i状态下,白色王能否进入位置(j,k)。列出所有状态下的地图。然后就是BFS了,当搜到黑色棋子时转换状态,直到黑色全吃完或者队列为空。
注意,当吃完一个黑色棋子后,可能白色王就处在了其他黑色棋子的攻击范围;地图用BOOL型可以减少占用空间。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int maxn=15;
const int INF=1e8;
int t;
struct node{
int x,y,num,key;
char ch;
}e[maxn];//存黑色棋子
bool map[1<<maxn][maxn][maxn];//dp[i][j][k],表示i状态下,(j,k)位置是否可走;i为状态压缩
int vis[maxn][maxn];
char a[maxn][maxn];
int n,m;
int dir[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};//马走的方式
int dir2[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};//王走的方式
int bfs(int x1,int y1,int key)//广搜
{
/*printf("key:%d\n",key);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
printf("%d",map[key][i][j]);
printf("\n");
}*/
node f,g;
int i,xx,yy;
queue<node>q;
memset(vis,-1,sizeof(vis));
f.x=x1;f.y=y1;f.num=0;f.key=key;
for(i=0;i<t;i++)
vis[e[i].x][e[i].y]=i;
q.push(f);
while(!q.empty())
{
f=q.front();
//printf("%d %d %d %d\n",f.x,f.y,f.num,f.key);
q.pop();
for(i=0;i<8;i++)
{
xx=f.x+dir2[i][0];yy=f.y+dir2[i][1];key=f.key;
if(xx<0||yy<0||xx>=n||yy>=m||map[key][xx][yy])continue;
if(vis[xx][yy]!=-1&&((1<<vis[xx][yy])&f.key))
{
key-=(1<<vis[xx][yy]);
if(key==0)return f.num+1;//全吃完输出结果
if(map[key][xx][yy])continue;//吃完后背其他棋子吃的情况
}
map[key][xx][yy]=true;
g.x=xx;g.y=yy;g.num=f.num+1,g.key=key;
q.push(g);
}
}
return INF;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int i,j,k,x,y,xx,yy;
t=0;
memset(map,false,sizeof(map));
for(i=0;i<n;i++)
scanf("%s",a[i]);
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(a[i][j]=='*'){x=i;y=j;}
else if(a[i][j]!='.')
{
e[t].x=i;
e[t].y=j;
e[t++].ch=a[i][j];
}
}
}
if(t==0){printf("0\n");continue;}//特判
//制作地图
int flag,c[4];
for(i=1;i<(1<<maxn);i++)//i为状态,化成二进制,第k位为0表示第k个棋子已经被吃,否则存在。
{
memset(vis,0,sizeof(vis));
for(j=0;j<t;j++)
{
if((1<<j)&i)
{
vis[e[j].x][e[j].y]=true;
//map[i][e[j].x][e[j].y]=true;
}
}
for(j=0;j<t;j++)
{
if((1<<j)&i)
{
if(e[j].ch=='K')
{
for(k=0;k<8;k++)
{
xx=e[j].x+dir[k][0];
yy=e[j].y+dir[k][1];
if(xx<0||yy<0||xx>=n||yy>=m)continue;
map[i][xx][yy]=true;
}
}
else if(e[j].ch=='R')
{
k=1;
flag=c[0]=c[1]=c[2]=c[3]=1;
while(flag)
{
flag=0;
if(c[0]&&e[j].x+k<n)
{
if(vis[e[j].x+k][e[j].y])c[0]=0;
else{map[i][e[j].x+k][e[j].y]=true;flag=true;}
}
if(c[1]&&e[j].x-k>=0)
{
if(vis[e[j].x-k][e[j].y])c[1]=0;
else{map[i][e[j].x-k][e[j].y]=true;flag=true;}
}
if(c[2]&&e[j].y+k<m)
{
if(vis[e[j].x][e[j].y+k])c[2]=0;
else{map[i][e[j].x][e[j].y+k]=true;flag=true;}
}
if(c[3]&&e[j].y-k>=0)
{
if(vis[e[j].x][e[j].y-k])c[3]=0;
else{map[i][e[j].x][e[j].y-k]=true;flag=true;}
}
k++;
}
}
else if(e[j].ch=='B')
{
flag=c[0]=c[1]=c[2]=c[3]=1;
k=1;
while(flag)
{
flag=0;
if(c[0]&&e[j].x+k<n&&e[j].y+k<m)
{
if(vis[e[j].x+k][e[j].y+k])c[0]=0;
else{map[i][e[j].x+k][e[j].y+k]=true;flag=true;}
}
if(c[1]&&e[j].x+k<n&&e[j].y-k>=0)
{
if(vis[e[j].x+k][e[j].y-k])c[1]=0;
else{map[i][e[j].x+k][e[j].y-k]=true;flag=true;}
}
if(c[2]&&e[j].x-k>=0&&e[j].y+k<m)
{
if(vis[e[j].x-k][e[j].y+k])c[2]=0;
else{map[i][e[j].x-k][e[j].y+k]=true;flag=true;}
}
if(c[3]&&e[j].x-k>=0&&e[j].y-k>=0)
{
if(vis[e[j].x-k][e[j].y-k])c[3]=0;
else{map[i][e[j].x-k][e[j].y-k]=true;flag=true;}
}
k++;
}
}
}
}
}
int ans=bfs(x,y,(1<<t)-1);
if(ans==INF)printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
/*
15 15
...............
B..............
.K.............
..R............
...R...........
....R..........
.....R.........
......R........
.......R.......
........R......
.........R.....
...........R...
.............R.
...............
..............*
*/