HDU5094 Maze

知识点:状态压缩、bfs

题目链接

题意

n ∗ m n*m nm网格中一段网格线可能代表钥匙扣型号为 g i g_i gi的锁住的门或墙(无法穿越网格线),方格可能代表存在多个型号为 q i q_i qi的钥匙。你可以四面移动、携带钥匙和打开门,求从 ( 1 , 1 ) (1,1) (1,1) ( n , m ) (n,m) (n,m)的最短移动距离。

思路

此题不为状压dp,仅为状压。
关键在于记录携带钥匙信息,注意到p很小,考虑维护第i位(逆序)为i号钥匙是否携带,压缩成范围小于 2 e 3 2e3 2e3( 2 10 2^{10} 210)的int类型,可作为数组下标,注意到n和m比较小,考虑bfs,且用vis[i][j][sta]表示是否有过走到(i,j)并且携带sta状态的钥匙的状态,可用于bfs剪枝,搜素范围为 n ∗ m ∗ 2 p < 4 e 6 n*m*2^p<4e6 nm2p<4e6

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e2+10;
const ll M=1e4+10;
const ll inf=0x3f3f3f3f3f3f3f3f;

ll n,m,p;
ll door[2][N][N];//door[0][i][j]:(i,j)方格的下边为钥匙孔为
//door[0][i][j]的状态,door[1][i][j]:(i,j)方格的右边为钥匙孔为
//door[1][i][j]的状态
bool vis[N][N][M];//同上述思路
ll key[N][N];//(i,j)方格上有钥匙的sta状态(再次提醒:同一格钥匙有多个)
ll k,s;
ll dir[4][2]= {
   -1,0,0,0,0,0,0,-1};//选择(i,j)方格的4个边界
ll dir2[4][2]= {
   -1,0,0,1,1,0,0,-1};//正常的方向数组

//bfs维护的信息
struct node
{
   
	ll x,y,key,step;//x,y:坐标,key:携带钥匙的状态,step:走过的距离
};

void init()
{
   
	for(ll i=0; i<=n+5; i++)
		for(ll j=0; j<=m+5; j++)
		{
   
			key[i][j]=door[0][i][j]=door[1][i][j]=0;
			for(ll k=0; k<(1<<p+1)+5; k++)
				vis[i][j][k]=false;
		}
}

void solve()
{
   
	init();
	scanf("%lld",&k);
	while(k--)
	{
   
		ll xa,ya,xb,yb,g;
		scanf("%lld%lld%lld%lld%lld",&xa,&ya,&xb,&yb,&g);
		if(xa==xb)//需要水平通过的门(门在竖直方向的网格线上)
		{
   
			ll pos=min(ya,yb);//取左边方格
			if(g==0) door[1][xa][pos]=-1;//有墙记为-1
			else door[1][xa][pos]=1<<g-1;//钥匙孔状态,(1<<i)为存在i+1号钥匙孔
		}
		else//同理
		{
   
			ll pos=min(xa,xb);//取上边方格
			if(g==0) door[0][pos][ya]=-1;
			else door[0][pos][ya]=1<<g-1;
		}
	}
	scanf("%lld",&s);
	while(s--)
	{
   
		ll x,y,q;
		scanf("%lld%lld%lld",&x,&y,&q);
		key[x][y]|=1<<q-1;//钥匙状态,(1<<i)为存在i+1号钥匙
	}
	if(n==1&&m==1)
	{
   
		puts("0");
		return;
	}
	queue<node> que;
	que.push({
   1,1,key[1][1],0});
	vis[1][1][key[1][1]]=true;
	while(!que.empty())
	{
   
		node t=que.front();
		que.pop();
		//当前状态的四个方向
		for(ll i=0; i<4; i++)
		{
   
			ll x=t.x+dir2[i][0],y=t.y+dir2[i][1];//枚举方向后的方格位置
			if(x<1||x>n||y<1||y>m) continue;
			ll xx=t.x+dir[i][0],yy=t.y+dir[i][1];//枚举方向后的网格线方向(建议手动模拟)
			//剪枝,条件为为墙或(不为空且没有对应钥匙),
			//i&1确定门的方向,对应方向数组,0为竖直方向,1为水平方向。
			if(door[i&1][xx][yy]==-1||
			door[i&1][xx][yy]!=0&&(t.key&door[i&1][xx][yy])==0) continue;
			ll sta=t.key|key[x][y];//更新状态
			if(vis[x][y][sta]) continue;
			vis[x][y][sta]=true;
			if(x==n&&y==m)
			{
   
				printf("%lld\n",t.step+1);
				return;
			}
			que.push({
   x,y,sta,t.step+1});
		}
	}
	puts("-1");
}

int main()
{
   
	while(~scanf("%lld%lld%lld",&n,&m,&p))
		solve();
	return 0;
}
全部评论

相关推荐

04-08 11:55
已编辑
巨人网络_招聘
投递巨人网络等公司6个岗位 > 笔试
点赞 评论 收藏
分享
小厂面经,也是我的处女面(30min)1.自我介绍2.spring&nbsp;boot的自动装配原理(好多类和接口的单词都忘了全称是啥了,就说了记得的单词,流程应该说对了吧)3.有用过redis吗?主要是用在实现什么功能(说了技术派用redis的zset来实现排行榜)5.有了解过Redisson吗?讲一下对于分布式锁的了解以及在什么场景下应用(说了秒杀场景)6.对mysql有了解吗?包括它的索引优化和创建(把想起来的全说了)7.了解设计模式吗?比如单例模式,为什么要使用单例模式,它的优点是什么(昨天刚看的设计模式)8.工厂模式有了解吗?主要的使用场景是?(也是昨天刚看的)9.场景题:有7个服务器,需要在早上十点定时的向数据库中的用户表中的用户发短信,如果做到发送的消息不重复,且如果发送失败了需要知道是到哪个用户失败了,这样下次就直接从这个用户开始(我答了用spring&nbsp;task来实现定时,用分布式锁来保证只有一份服务器可以发送消息,用消息队列来存储消息,然后用消息确认机制来保证错误信息的记录,以及在数据库或者业务层面完成消息消费的幂等性)10.场景题:如果在系统启动的时间就将数据库的所有用户相关的信息都读到一个hashmap中(这个没啥思路,没答好)27届的投了一个星期终于有一个面试了,大部分公司都只招26的
inari233:已oc,拒了
查看9道真题和解析
点赞 评论 收藏
分享
牛客316659795号:不是,证明hr初筛已经过了,要投给部门筛一遍
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务