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;
}
全部评论

相关推荐

06-27 12:54
已编辑
门头沟学院 Java
累了,讲讲我的大学经历吧,目前在家待业。我是一个二本院校软件工程专业。最开始选专业是觉得计算机感兴趣,所以选择了他。本人学习计算机是从大二暑假结束开始的,也就是大三开始。当时每天学习,我个人认为Java以及是我生活的一部分了,就这样持续学习了一年半,来到了大四上学期末,大概是在12月中旬,我终于找的到了一家上海中厂的实习,但我发现实习生的工作很枯燥,公司分配的活也不多,大多时间也是自己在自学。就这样我秋招末才找到实习。时间来到了3月中旬,公司说我可以转正,但是转正工资只有7000,不过很稳定,不加班,双休,因为要回学校参加答辩了,同时当时也是心高气傲,认为可以找到更好的,所以放弃了转正机会,回学校准备论文。准备论文期间就也没有投递简历。然后时间来到了5月中旬,这时春招基本也结束了,然后我开始投递简历,期间只是约到了几家下场面试。工资也只有6-7k,到现在我不知道该怎么办了。已经没有当初学习的心劲了,好累呀,但是又不知道该干什么去。在家就是打游戏,boss简历投一投。每天日重一次。26秋招都说是针对26届的人,25怎么办。我好绝望。要不要参加考公、考研、央国企这些的。有没有大佬可以帮帮我。为什么感觉别人找工作都是顺其自然的事情,我感觉自己每一步都在艰难追赶。八股文背了又忘背了又忘,我每次都花很长时间去理解他,可是现在感觉八股、项目都忘完了。真的已经没有力气再去学习了。图片是我的简历,有没有大哥可以指正一下,或者说我应该走哪条路,有点不想在找工作了。
码客明:太累了就休息一下兄弟,人生不会完蛋的
如果实习可以转正,你会不...
点赞 评论 收藏
分享
代码飞升:别用口语,后端就写后端,前端就写前端,最后别光后悔
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务