Leetcode 864. 获取所有钥匙的最短路径

题目描述:

给定一个二维网格 grid。 "." 代表一个空房间, "#" 代表一堵墙, "@" 是起点,("a""b", ...)代表钥匙,("A""B", ...)代表锁。

我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。

假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。

返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。

 

示例 1:

输入:["@.a.#","###.#","b.A.B"]
输出:8

示例 2:

输入:["@..aA","..B#.","....b"]
输出:6

 

提示:

  1. 1 <= grid.length <= 30
  2. 1 <= grid[0].length <= 30
  3. grid[i][j] 只含有 '.''#''@''a'-'f' 以及 'A'-'F'
  4. 钥匙的数目范围是 [1, 6],每个钥匙都对应一个不同的字母,正好打开一个对应的锁。

java:

/**思路:三维BFS问题,三个维度分别为行,列,当前获得的钥匙
把遍历过程中的状态唯一地用当前行列和获得的钥匙这三个信息表示,设计一个state变量用来表示状态
根据题目数据量的大小,行列最多30,钥匙最多6,所以行列可以分别用5bit表示,钥匙用6bit表示,一个int就够了
为了方便,使用8+8+8的形式
**/
class Solution {
    public int shortestPathAllKeys(String[] grid) {
        
        int r=grid.length;
        int c=grid[0].length();
        LinkedList<Integer> que=new LinkedList<>();
        boolean[][][] visit=new boolean[r][c][64];
        int lock=0;
        //获得BFS结束条件
        for (int i=0;i<grid.length;i++){
            for (int j=0;j<grid[0].length();j++){
                int tmpchar=grid[i].charAt(j)-'A';
                if(tmpchar>=0&&tmpchar<=6){
                    lock|=1<<tmpchar;
                }
                //获得开始位置
                else if(grid[i].charAt(j)=='@'){
                    que.offer(i<<16|j<<8);
                    visit[i][j][0]=true;
                }
            }
        }
        int[] dirc={0,-1,0,1,0};
        int res=0;
        while(!que.isEmpty()){
            int size=que.size();
            while(size-->0){
                int tmp=que.poll();
            int x=tmp>>16;
            int y=(tmp>>8)&0xFF;
            int key=tmp&0xFF;
                //如果当前状态获得的钥匙满足要求,退出
            if(key==lock)return res;
                //上下左右遍历
            for (int i=0;i<4;i++){
                //对每一个方向找一个新状态
                int nx=x+dirc[i];
                int ny=y+dirc[i+1];
                if((nx>r-1)||(nx<0)||(ny>c-1)||(ny<0)){
                    continue;
                }
                char tmpchar=grid[nx].charAt(ny);
                //如果碰墙
                if(tmpchar=='#')continue;
                //如果遇到的锁没有钥匙
                if ((tmpchar>='A')&&(tmpchar<='F')&&((key&(1<<(tmpchar-'A')))==0))continue;
                int nkey=0;
                //如果遇到新锁
               if(tmpchar>='a'&&tmpchar<='f'){
                  nkey=key|(1<<(tmpchar-'a'));
               }
                //如果遇到普通空房间或者遇到有钥匙的锁
                else {
                    nkey=key;
                }
                //判断此时找到的状态有没有被访问过
                if(visit[nx][ny][nkey])continue;
                visit[nx][ny][nkey]=true;
                que.offer(nx<<16|ny<<8|nkey);
            }
            }
            ++res; 
        }
        return -1;
    }
}

cpp:

// Author: Huahua
// Running time: 8 ms
class Solution {
public:
  int shortestPathAllKeys(vector<string>& grid) {
    int m = grid.size();
    int n = grid[0].size();    
    int all_keys = 0;
    queue<int> q;
    vector<vector<vector<int>>> seen(m, vector<vector<int>>(n, vector<int>(64, 0)));
        
    // Init
    for (int i = 0; i < m; ++i)
      for (int j = 0; j < n; ++j) {
        const char c = grid[i][j];
        if (c == '@') {
          q.push((j << 16) | (i << 8));
          seen[i][j][0] = 1;
        } else if (c >= 'a' && c <= 'f') {
          all_keys |= (1 << (c - 'a'));
        }
      }
    
    const vector<int> dirs{-1, 0, 1, 0, -1};
    
    int steps = 0;
    while (!q.empty()) {
      int size = q.size();
      while (size--) {
        int s = q.front(); q.pop();
        int x = s >> 16;
        int y = (s >> 8) & 0xFF;
        int keys = s & 0xFF;        
        if (keys == all_keys) return steps;
        for (int i = 0; i < 4; ++i) {
          int nkeys = keys;
          int nx = x + dirs[i];
          int ny = y + dirs[i + 1];          
          if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
          const char c = grid[ny][nx];          
          if (c == '#') continue; // Wall
          // Do not have the key.
          if (c >= 'A' && c <= 'F' && !(keys & (1 << (c - 'A')))) continue;
          // Update the keys we have.
          if (c >= 'a' && c <= 'f') nkeys |= (1 << (c - 'a'));
          if (seen[ny][nx][nkeys]) continue;
          q.push((nx << 16) | (ny << 8) | nkeys);
          seen[ny][nx][nkeys] = 1;
        }
      }
      ++steps;
    }
    return -1;
  }
};

Python:

# Author: Huahua, 390 ms
class Solution:
  def shortestPathAllKeys(self, grid):
    m, n = len(grid), len(grid[0])
    all_keys = 0
    seen = [[[None]* 64 for _ in range(n)] for _ in range(m)]
    q = collections.deque()
    for i in range(m):
      for j in range(n):
        c = grid[i][j]
        if c == '@':
          q.append((j << 16) | (i << 8))
          seen[i][j][0] = 1
        elif c >= 'a' and c <= 'f':
          all_keys |= (1 << (ord(c) - ord('a')))
          
    dirs = [-1, 0, 1, 0, -1]
    steps = 0
    while q:
      size = len(q)
      while size > 0:
        size -= 1
        s = q.popleft()
        x = s >> 16
        y = (s >> 8) & 0xff
        keys = s & 0xff        
        if keys == all_keys: return steps
        for i in range(4):
          nx, ny, nkeys = x + dirs[i], y + dirs[i + 1], keys          
          if nx < 0 or nx >= n or ny < 0 or ny >= m: continue
          c = grid[ny][nx]
          if c == '#': continue
          if c in string.ascii_uppercase and keys & (1 << (ord(c) - ord('A'))) == 0: continue
          if c in string.ascii_lowercase: nkeys |= (1 << (ord(c) - ord('a')))
          if seen[ny][nx][nkeys]: continue
          q.append((nx << 16) | (ny << 8) | nkeys)
          seen[ny][nx][nkeys] = 1
      steps += 1
    return -1

 

posted @ 2019-06-05 23:45  pibaba  阅读(665)  评论(0编辑  收藏  举报