一、题目
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。
锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
 
 来源:力扣(LeetCode)
 链接:https://leetcode.cn/problems/open-the-lock/description/
二、C++解法
我的思路及代码
采用BFS,当前状态到下一个状态无非就是4个位置往上拨,或者下拨。那么把这个状态抽象为一张图,即是从根节点出发有8个子节点的多叉树,最后我们要找的target就在其中某个结点。因为要找到最短的路径,所以我们采用BFS齐头并进的办法。由于题目中提到了 deadends 所以我们可以采用一组哈希表来存储 deadends 同时来记录我们已经访问到的组合,以免走回头路。具体代码如下。
class Solution {
public:
    void plusOne(string &ans,int index){
        if(ans[index] == '9')
        ans[index] = '0';
        else ans[index]++;
    }
    void minusOne(string &ans,int index){
        if(ans[index] == '0')
        ans[index] = '9';
        else ans[index]--;
    }
    int openLock(vector<string>& deadends, string target) {
        if(target == "0000")
        return 0;
        queue<string> q;
        unordered_map<string,bool> map;
        for(int k=0;k<deadends.size();k++){
            if(deadends[k]=="0000")
            return -1;
            else{
                map[deadends[k]] = true;
            }
        }
        q.push("0000");
        int ans=0;
        string s1,s2;
        map["0000"] = true;
        while(q.size()){
            int size = q.size();
            ans++;
            for(int i=0;i<size;i++){      
                for(int j=0;j<4;j++){
                    s1 = q.front();
                    plusOne(s1,j);
                    if(s1==target)
                    return ans;
                    if(!map[s1]){
                        map[s1]=true;
                        q.push(s1);
                    }
                } 
                for(int j=0;j<4;j++){
                    s2 = q.front();
                    minusOne(s2,j);
                    if(s2==target)
                    return ans;
                    if(!map[s2]){
                        map[s2]=true;
                        q.push(s2);
                    }
                }
                q.pop();
            }
        }
        return -1;
    }
};
 

官方参考代码
启发式搜索

 
struct AStar {
    // 计算启发函数
    static int getH(const string& status, const string& target) {
        int ret = 0;
        for (int i = 0; i < 4; ++i) {
            int dist = abs(int(status[i]) - int(target[i]));
            ret += min(dist, 10 - dist);
        }
        return ret;
    };
    AStar(const string& status, const string& target, int g): status_{status}, g_{g}, h_{getH(status, target)} {
        f_ = g_ + h_;
    }
    bool operator< (const AStar& that) const {
        return f_ > that.f_;
    }
    string status_;
    int f_, g_, h_;
};
class Solution {
public:
    int openLock(vector<string>& deadends, string target) {
        if (target == "0000") {
            return 0;
        }
        unordered_set<string> dead(deadends.begin(), deadends.end());
        if (dead.count("0000")) {
            return -1;
        }
        auto num_prev = [](char x) -> char {
            return (x == '0' ? '9' : x - 1);
        };
        auto num_succ = [](char x) -> char {
            return (x == '9' ? '0' : x + 1);
        };
        auto get = [&](string& status) -> vector<string> {
            vector<string> ret;
            for (int i = 0; i < 4; ++i) {
                char num = status[i];
                status[i] = num_prev(num);
                ret.push_back(status);
                status[i] = num_succ(num);
                ret.push_back(status);
                status[i] = num;
            }
            return ret;
        };
        priority_queue<AStar> q;
        q.emplace("0000", target, 0);
        unordered_set<string> seen = {"0000"};
        while (!q.empty()) {
            AStar node = q.top();
            q.pop();
            for (auto&& next_status: get(node.status_)) {
                if (!seen.count(next_status) && !dead.count(next_status)) {
                    if (next_status == target) {
                        return node.g_ + 1;
                    }
                    q.emplace(next_status, target, node.g_ + 1);
                    seen.insert(move(next_status));
                }
            }
        }
        return -1;
    }
};
 
启发式搜索不讨论时空复杂度。



















