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;
}