洛谷P4011 孤岛营救问题【状压DP+搜索】

题目描述 https://www.luogu.org/problemnew/show/P4011
这道题看起来很复杂,有各种的门和钥匙,还有过不去的墙。仔细分析还是可以做出来的。
先说明一下数组的含义
1.用map[x][y][xx][yy]表示坐标(x,y)与坐标(xx,yy)之间有墙还是钥匙,-1为墙,是几就是有几号的门。
2.ky[x][y][num[x][y]]表示(x,y)上第num[x][y]把钥匙。
3.fl[x][y][k]标记数组,k为钥匙的状态
本题要用到优先队列,比较方便
注意:无法走出要输出-1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue> 
using namespace std;
int map[15][15][15][15],ky[15][15][150],num[15][15];
int n,m;
bool fl[15][15][(1<<15)];//标记 最后一位为钥匙的状态 
struct mp{
	int x,y,step,key;//位置 步数 钥匙的状态 
};
queue <mp> d;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int bfs()
{
	int nows=0;
	for(int i=1;i<=num[1][1];i++)
	  nows|=(1<<(ky[1][1][i]-1));//将第一格的钥匙记录下来 
	fl[1][1][nows]=1;//做标记 
	d.push((mp){1,1,0,nows});
	while(!d.empty())
	{
		mp now=d.front();
		d.pop();
		if(now.x==n&&now.y==m) return now.step;
		for(int i=0;i<=3;i++)
		{
			int xx=now.x+dx[i];
			int yy=now.y+dy[i];
			if(xx<1||xx>n||yy<1||yy>m) continue;
			int t=map[now.x][now.y][xx][yy];
			if(t==-1) continue;//有墙 
			if((t!=0)&&((now.key&(1<<(t-1)))==0)) continue;//有门但没有门的钥匙 
			int kk=now.key;//当前有的钥匙状态 
			for(int j=1;j<=num[xx][yy];j++)
			  kk|=(1<<(ky[xx][yy][j]-1));//加上这一格的钥匙 
			if(fl[xx][yy][kk]==1) continue; 
			fl[xx][yy][kk]=1;
			d.push((mp){xx,yy,now.step+1,kk});
		}
	}
	return -1;//无法走出 
}
int main()
{ int p,k,s;
   
   scanf("%d%d%d",&n,&m,&p);
   scanf("%d",&k);
   while(k--)
   {
   	  int x,y,xx,yy,g;
	  scanf("%d%d%d%d%d",&x,&y,&xx,&yy,&g);
	  if(g==0) map[x][y][xx][yy]=map[xx][yy][x][y]=-1;//有墙 
	  else map[x][y][xx][yy]=map[xx][yy][x][y]=g;//有g号门  
   }
   scanf("%d",&s);
   while(s--)
   {
   	  int x,y,p;
   	  scanf("%d%d%d",&x,&y,&p); 
	  ky[x][y][++num[x][y]]=p;//(x,y)上第num[x][y]把钥匙为p号钥匙 
   }
   printf("%d",bfs());
	
  return 0;	
}

猜你喜欢

转载自blog.csdn.net/qq_42920122/article/details/87981739
今日推荐