【双端队列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();
} 


posted @ 2020-08-18 15:59  Hyx'  阅读(134)  评论(0编辑  收藏  举报