【双端队列bfs 网格图建图】拯救大兵瑞恩
传送门
题意
\(N\times M\)的网格图,\(K\)个门,用\((x_{1},y_{1},x_{2},y_{2},c)\),表示从点\((x_{1},y_{1})\)到点\((x_{2},y_{2})\)的边上有种类为\(c\)的门
有\(s\)把钥匙,描述为\((x,y,c)\),表示点\((x,y)\)上有一把可以打开门\(c\)的钥匙,求从点\((1,1)\)走到\((N,M)\)的最短路径
数据范围
\(\begin{array}{l}\left|X_{i 1}-X_{i 2}\right|+\left|Y_{i 1}-Y_{i 2}\right|=1 \\ 0 \leq G_{i} \leq P \\ 1 \leq Q_{i} \leq P \\ 1 \leq N, M, P \leq 10 \\ 1 \leq k \leq 150\end{array}\)
题解
将每一步拿取的钥匙通过二进制状态压缩来表示,最多有\(10\)种钥匙,第\(0,1,\dots p-1\)依次表示第\(1,2,\dots ,p\) 种钥匙,
- 先将网格图的每一个格点表示为一个具体的标号,先把有门边用前向星建边,并做上标记,边权表示的是门的种类
- 然后对每一个网格可能走向的四个方向进行建边,如果当前走的边没有门的标记就建立即可
- 对于有钥匙的结点进行标记
然后利用双端队列进行bfs
- 先将所有节点,所有钥匙状态的距离初始化为\(INF\)
- 每个格点只有有钥匙和无钥匙两个状态,如果有钥匙就异或后对当前距离取个小值,然后放入队列的头部
- 考虑所有的出边,如果出边的点的距离大于当前点的距离\(+1\)就入队尾
- 如果当前点的编号是\((N<M)\)的编号那么就到达了直接返回距离即可
- 最后队列空后,即不可以到达目标点返回\(-1\)
Code
#include<bits/stdc++.h>
using namespace std;
#define close ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
#define fi first
#define se second
#define ll long long
#define pb push_back
typedef pair<long long,long long> pll;
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef vector<long long> vll;
typedef double db;
const ll mod=1e9+7;
const int N=11,M=N*N,E=400;
ll powmod(ll a,ll b,ll p){ll res=1;a%=p;while(b){if(b&1) res=res*a%p;a=a*a%p;b>>=1;}return res;}
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
struct Edge
{
int to,val,ne;
}edge[E];
int h[M],idx;
int n,m,p,k,s;
int g[N][N];
bool st[M][1<<10];
int d[E][1<<10];
int key[M];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
deque<pii>q;
set<pii>mp;
int t;
void add(int a,int b,int c)
{
edge[++idx].to=b;
edge[idx].ne=h[a];
edge[idx].val=c;
h[a]=idx;
}
void build()
{
rep(x,1,n+1) rep(y,1,m+1) rep(u,0,4)
{
int px=x+dx[u],py=y+dy[u];
if(!px || px>n || !py ||py>m) continue;
int from=g[x][y],to=g[px][py];
if(mp.count({from,to})==0)
add(from,to,0);
}
}
int bfs()
{
memset(d,0x3f,sizeof d);
d[1][0]=0;
q.push_back({1,0});
while(q.size())
{
pii tmp=q.front();
q.pop_front();
int now_loc=tmp.fi,now_key=tmp.se;
if(st[now_loc][now_key]) continue;
st[now_loc][now_key] = 1;
if(now_loc == n * m) return d[now_loc][now_key];
if(key[now_loc])
{
int state = now_key | key[now_loc];
if(d[now_loc][state] > d[now_loc][now_key])
{
d[now_loc][state]=d[now_loc][now_key];
q.push_front({now_loc,state});
}
}
for(int i = h[now_loc];i;i = edge[i].ne)
{
int to = edge[i].to;
if(edge[i].val && ! (now_key>>edge[i].val -1 & 1)) continue;
if(d[to][now_key] > d[now_loc][now_key]+1)
{
d[to][now_key] = d[now_loc][now_key]+1;
q.push_back({to,now_key});
}
}
}
return -1;
}
void solve()
{
cin>>n>>m>>p>>k;
rep(i,1,n+1) rep(j,1,m+1)
g[i][j]=++t;
while(k--)
{
int x1,y1,x2,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
int a=g[x1][y1],b=g[x2][y2];
mp.insert({a,b}),mp.insert({b,a});
if(c)
add(a,b,c),add(b,a,c);
}
cin>>s;
while(s--)
{
int x,y,c;
cin>>x>>y>>c;
key[g[x][y]] |= 1 << c -1;
}
build();
cout<<bfs()<<endl;
}
int main(){
close
solve();
}