zoukankan      html  css  js  c++  java
  • 【图论】拯救大兵瑞恩

    传送门:https://www.acwing.com/problem/content/1133/

    这题的建图方式相当地恶心...不过这题的思想还是很有趣的。

    分析

    假如没有门,朴素的bfs就足够了,但这题有门,所以我们考虑增加一维状态,用来记录当前节点拥有的钥匙的情况。

    对于当前节点(房间):

    • 如果有钥匙,基于贪心的思想,把钥匙全部拿上肯定不亏,所以状态变成拿起当前节点的所有钥匙,注意到这个过程并没有走动,所以转移时候的“边权”为 (0)
    • 如果没有钥匙,什么都干不了,不用转移。

    然后可以从当前房间走到附近的房间,进行状态转移:这个时候,“边权”为 (1)

    从上面的讨论可知,因为当前节点转移过程的边权只可能是 (0,1) ,故考虑双端队列广搜,而这个过程完全类似于dijkstra

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    #define x first
    #define y second
    typedef pair<int,int> PII;
    
    const int N=20, M=400;
    
    struct node{
    	int to,next,w;
    }e[M];
    int head[M],tot;
    void add(int u,int v,int w){e[tot].to=v;e[tot].w=w;e[tot].next=head[u];head[u]=tot++;}
    
    int g[N][N];
    set<PII> edges;
    int key[M];
    
    int n,m,p,s;
    
    int dx[]={-1,0,1,0};
    int dy[]={0,1,0,-1};
    
    void build(){
    	int k;
        cin>>k;
        while(k--){
        	int x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2;
        	int c; cin>>c;
        	int u=g[x1][y1], v=g[x2][y2];
        	if(c) add(u,v,c), add(v,u,c);
        	edges.insert({u,v}); edges.insert({v,u});
        }
        
        cin>>s;
        while(s--){
        	int x,y,w; cin>>x>>y>>w;
        	key[g[x][y]]|=1<<(w-1);
        }
        
        for(int x=1;x<=n;x++)
        	for(int y=1;y<=m;y++)
        		for(int op=0;op<4;op++){
        			int kx=x+dx[op], ky=y+dy[op];
        			if(!kx || kx>n || !ky || ky>m) continue;
        			int p1=g[x][y], p2=g[kx][ky];
        			if(!edges.count({p1,p2})) add(p1,p2,0), add(p2,p1,0);
        		}
    }
    
    int d[M][1<<12];
    bool vis[M][1<<12];
    int bfs(){
    	memset(d,0x3f,sizeof d);
        deque<PII> q;
        q.push_front({1,0});
        d[1][0]=0;
        
        while(q.size()){
        	auto hd=q.front(); q.pop_front();
        	int ver=hd.x;
        	
        	if(vis[ver][hd.y]) continue;
        	vis[ver][hd.y]=true;
        	
        	if(ver==n*m) return d[ver][hd.y];
        	
        	int st=hd.y;
        	if(key[ver]){
        		st|=key[ver];
        		if(d[ver][st]>d[ver][hd.y]){
        			d[ver][st]=d[ver][hd.y];
        			q.push_front({ver,st});
        		}
        	}
        	
        	for(int i=head[ver];~i;i=e[i].next){
        		int go=e[i].to;
        		if(e[i].w && !(st>>(e[i].w-1)&1)) // 障碍
        			continue;
        		
        		if(d[go][st]>d[ver][hd.y]+1){
        			d[go][st]=d[ver][st]+1;
        			q.push_back({go,st});
        		}
        	}
        }
        
        return -1;
    }
    
    int main(){
    	memset(head,-1,sizeof head);
        cin>>n>>m>>p;
        
        for(int i=1,t=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                g[i][j]=t++; // 记录点的编号
        
        build(); // 建图
        
        cout<<bfs()<<endl;
        
        return 0;
    }
    
  • 相关阅读:
    第三章:软件也要拼脸蛋-UI 开发的点点滴滴
    第二章:先从看得到的入手-探究活动
    第一章:开始启程-你的第一行Android代码
    367. Valid Perfect Square
    逆向工程
    lombok日志包的使用
    Mysql(一)
    数据库
    mvc+三层架构
    Maven
  • 原文地址:https://www.cnblogs.com/Tenshi/p/14534358.html
Copyright © 2011-2022 走看看