• 【双端队列bfs 网格图建图】拯救大兵瑞恩


    传送门

    题意

    (N imes 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))的最短路径

    数据范围

    (egin{array}{l}left|X_{i 1}-X_{i 2} ight|+left|Y_{i 1}-Y_{i 2} ight|=1 \ 0 leq G_{i} leq P \ 1 leq Q_{i} leq P \ 1 leq N, M, P leq 10 \ 1 leq k leq 150end{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();
    } 
    
    
    
  • 相关阅读:
    po教学001
    肖sir__ 金牌高级讲师__下载视频方法
    肖sir__ 金牌高级讲师__html下载收费音乐方法
    肖sir_少儿编程了解(001)
    【CF375D】Tree and Queries
    【CF1063F】String Journey
    【洛谷P6071】Treequery
    【ARC122E】Increasing LCMs
    【ARC122C】Calculator
    【牛客练习赛84 E】牛客推荐系统开发之标签重复度
  • 原文地址:https://www.cnblogs.com/hhyx/p/13524033.html
Copyright © 2020-2023  润新知