(BFS+状压)AcWing1131拯救大兵瑞恩

AcWing1131拯救大兵瑞恩

思路:

先想到是用一个结构体存一个格子四周的情况和钥匙的情况,然后每次遇到门的时候要把自己有的钥匙和需要的比较。但后来想到钥匙最多只有十个,所以可以用状态压缩,每个点的钥匙和自己的钥匙可以用二进制数表示,拿钥匙就用按位或;遇到门就用按位与判断。
然后我又用结构体记录一个点四周是否有墙,结果愉快的MLE了。后来把数据自己测的时候发现跑不出结果。在后面的一个小时内,我经历了TLE,CE,IE,WA等一系列奇怪的错误结果。
因为可以走回头路,所以我开始就没有用vis数组。但是我们又可以考虑到,我们走回头路的目的是拿钥匙,所以如果我们如果遇到走到这点时,拥有这个钥匙的状态已经存在了,就没必要再走一次了,所以用一个vis[x][y][key]来记录(x,y)点拥有钥匙状态为key的情况是否已经走过。
又发现如果我们要记录一个点的四周情况是比较麻烦的,我们只需要用一个数组wall[x1][y1][x2][y2]就可以记录两个相邻格子之间的情况了。

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
const int N=1e6+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
struct edge
{
	int x,y,t,own;
};
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}},vis[20][20][1<<11]={0};
int wall[20][20][20][20],key[20][20]={0};
int n,m,p;
int bfs()
{
	queue<edge> q;
	edge now,pre,temp;
	now.x=1;
	now.y=1;
	now.t=0;
	now.own=key[1][1];
	q.push(now);
	while(!q.empty())
	{
		pre=q.front();
		q.pop();
		if(pre.x==n && pre.y==m)	
			return pre.t;
		int i;
		for(i=0;i<4;i++)
		{
			temp.x=pre.x+dis[i][0];
			temp.y=pre.y+dis[i][1];
			if(temp.x>=1 && temp.x<=n && temp.y>=1 && temp.y<=m)
			{
				int need=wall[pre.x][pre.y][temp.x][temp.y];
				if(need==0)
					continue;
				if(need==-1 || (pre.own & (1<<need))!=0)
				{
					temp.own=pre.own|key[temp.x][temp.y];
					temp.t=pre.t+1;
					if(vis[temp.x][temp.y][temp.own]==0)
					{
						vis[temp.x][temp.y][temp.own]=1;
						q.push(temp);
					}	
				}	
			}	
		}
	}
	return -1;
}
int main()
{
	int i,j,l,k,t;
	memset(wall,-1,sizeof(wall));
	scanf("%d%d%d%d",&n,&m,&p,&k);
	for(i=1;i<=k;i++)
	{
		int x1,y1,x2,y2,s;
		scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&s);
		wall[x1][y1][x2][y2]=s;
		wall[x2][y2][x1][y1]=s;
	}
	scanf("%d",&t);
	for(i=1;i<=t;i++)
	{
		int x,y,m;
		scanf("%d%d%d",&x,&y,&m);
		key[x][y]=key[x][y]|(1<<m);
	}
	int ans=bfs();
	printf("%d\n",ans);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值