847. 访问所有节点的最短路径 - 力扣(LeetCode)
一个很重要的优化,起点一定是连接节点个数最少的,这样保证了路径最短
class Solution
{
public:
struct state
{
int now;
int loc;
int dist; //这是比较用的dist,dis + astar
//返回值是bool类型,重载运算符<, 右边参数为不能修改的引用,这个函数不会修改结构体的值
bool operator<(const state &rhs) const
{
return dist > rhs.dist;
}
};
int astar(int loc)
{
// 1的数量表示还需要去哪些点
int res = 0;
while (loc)
{
loc -= (loc & -loc);
res++;
}
return res;
}
int shortestPathLength(vector<vector<int>> &graph)
{
int n = graph.size();
int ans = 0x3f3f3f3f;
int mi = 0x3f3f3f3f;
for(auto &p: graph)
mi=min(mi, (int)p.size());
unordered_map<int, unordered_map<int, int>> dist;
priority_queue<state, vector<state>> q;
for (int i = 0; i < n; i++)
{
//起点i
if(graph[i].size() != mi) continue;
int loc = ((1 << n) - 1) ^ (1 << i);
dist[i][loc] = 0;
q.push({i, loc, n - 1});
}
while (q.size())
{
auto t = q.top();
q.pop();
int u = t.now;
int d = astar(t.loc);
if(d > astar(t.loc) + dist[u][t.loc]) continue;
if (astar(t.loc) == 0)
{
ans = min(ans, t.dist);
break;
}
for (int j = 0; j < graph[u].size(); j++)
{
int nx = graph[u][j];
int nloc = t.loc;
if (t.loc & (1 << nx))
nloc ^= (1 << nx);
//如果nx位置没有走过,更新nloc
// if(st[nx][nloc]) continue;
if (!dist.count(nx) || !dist[nx].count(t.loc) || dist[nx][nloc] > dist[u][t.loc] + 1)
{
dist[nx][nloc] = dist[u][t.loc] + 1;
q.push({nx, nloc, dist[nx][nloc] + astar(nloc)});
}
}
}
return ans;
}
};
127. 单词接龙 - 力扣(LeetCode)
class Solution
{
public:
typedef pair<int,int> PII;
bool edgeif(string a, string b)
{
unordered_map<char, int> mp;
if (a == b)
return true;
bool flag = false;
for (int i = 0; i < a.size(); i++)
{
if (a[i] != b[i] && !flag)
{
flag = true;
}
else if(a[i] != b[i] && flag)
return false;
}
return true;
}
int astar(int _a, int _end, vector<string> &wordList)
{
string a = wordList[_a];
string end = wordList[_end];
if (a.size() != end.size())
return 0x3f3f3f3f;
int res = 0;
for (int i = 0; i < a.size(); i++)
{
if (a[i] != end[i])
res++;
}
return res;
}
int ladderLength(string beginWord, string endWord, vector<string> &wordList)
{
wordList.push_back(beginWord);
if (find(wordList.begin(), wordList.end(), endWord) == wordList.end())
return 0;
wordList.push_back(endWord);
int n = wordList.size();
unordered_map<string, int> mp;
for(int i = 0; i < wordList.size(); i ++) mp[wordList[i]] = i;
int dist[n];
bool st[n];
memset(dist, 0x3f3f3f3f, sizeof(dist));
memset(st, 0, sizeof(st));
dist[n - 2] = 0;
st[n - 2] = true;
priority_queue<PII, vector<PII>, greater<PII>> q;
q.push({astar(n - 2, n - 1, wordList), n - 2});
while (q.size())
{
auto t = q.top();
q.pop();
int u = t.second;
if(u == n - 1) break;
st[u] = false;
for(int i = 0; i < 26; i ++ )
for(int j = 0; j < wordList[u].size(); j ++)
{
string now = wordList[u];
if(now[j] != 'a' + i)
{
now[j] = 'a' + i;
}
if(!mp.count(now))
continue;
int to = mp[now];
if (st[to])
continue;
int d_a = astar(to, n - 1, wordList);
if (dist[to] > dist[u] + 1 )
{
dist[to] = dist[u] + 1;
st[to] = true;
q.push({dist[to] + d_a, to});
}
}
}
if (dist[n - 1] >= 0x3f3f3f3f)
return 0;
return dist[n - 1] + 1;
}
};
752. 打开转盘锁 - 力扣(LeetCode)
class Solution
{
public:
// 0 1 2 3 4 5 6 7 8 9
typedef pair<int, string> PIS;
int astar(string a, string target)
{
int res = 0;
for (int i = 0; i < a.size(); i++)
{
res += min((a[i] - target[i] + 10) % 10, (-a[i] + target[i] + 10) % 10);
}
return res;
}
int openLock(vector<string> &deadends, string target)
{
if(target == "0000") return 0;
//每个数字可以有两个旋转方式,每次可以选择一个位置旋转,不能旋转到死亡数字
priority_queue<PIS, vector<PIS>, greater<PIS>> q;
unordered_map<string, bool> st;
unordered_map<string, int> dist;
for (auto p : deadends)
st[p] = true; //标记死亡状态
if (!st["0000"])
{
q.push({astar("0000", target), "0000"}); //初始状态
st["0000"] = true;
}
bool flag = false;
while (!q.empty())
{
auto u = q.top();
string t = u.second;
st[t] = false;
q.pop();
if (t == target)
{
break;
}
for (int i = 0; i < 4; i++) //找一个位置
{
string ne = t;
ne[i] = (t[i] - '0' + 1) % 10 + '0';
if (!st[ne])
{
if (!dist.count(ne) || dist[ne] > dist[t] + 1)
{
dist[ne] = dist[t] + 1;
st[ne] = true;
q.push({dist[ne] + astar(ne, target), ne});
}
}
ne[i] = (t[i] - '0' - 1 + 10) % 10 + '0';
if (!st[ne])
{
if (!dist.count(ne) || dist[ne] > dist[t] + 1)
{
dist[ne] = dist[t] + 1;
st[ne] = true;
q.push({dist[ne] + astar(ne, target), ne});
}
}
}
}
if (dist.count(target))
return dist[target];
else
return -1;
}
};
773. 滑动谜题 - 力扣(LeetCode)
#define x first
#define y second
class Solution {
public:
string end = "123450";
int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
typedef pair<int, string> PIS;
int f(string s)
{
int res = 0;
for(int i = 0; i < s.size(); i ++ )
if(s[i] != '0')
{
int p = s[i] - '1';
res += abs(i / 3 - p / 3) + abs(i % 3 - p % 3);
}
return res;
}
int bfs(string state)
{
unordered_map <string, int> dist;
priority_queue <PIS, vector<PIS>, greater<PIS>> q;
unordered_map <string, bool> st;
st[state] = true;
q.push({f(state), state});//将初始状态入栈
dist[state] = 0;
while(q.size())
{
auto t = q.top();
q.pop();
string s = t.y;
if(s == end) break;
st[s] = false;
int x, y;
for(int i = 0; i < 6; i ++ )//找到0的位置
{
if(s[i] == '0')
{
x = i / 3;
y = i % 3;
break;
}
}
for(int i = 0; i < 4; i ++ )//和上下左右交换
{
int nx = x + dx[i], ny = y + dy[i];
if(nx < 0 || ny < 0 || nx > 1 || ny > 2) continue;//两行三列
string source = s;
swap(source[x * 3 + y], source[nx * 3 + ny]);
if(st[source]) continue;
if(!dist.count(source) || dist[source] > dist[s] + 1)//dist是无穷大或者是比s + 1大
{
dist[source] = dist[s] + 1;
q.push({f(source) + dist[source], source});
}
}
}
return dist[end];
}
int slidingPuzzle(vector<vector<int>>& board) {
//A* 多次入栈,终点出栈只能确定终点的最短路径
//判断是否有解
string start;
for(auto &t : board)
for(auto &x : t)
start += x + '0';
int cnt = 0;
for(int i = 0; i < 6; i ++ )
for(int j = i + 1; j < 6; j ++ )
{
if(start[i] != '0' && start[j] != '0' && start[j] < start[i]) cnt ++;
}
if(cnt % 2) return -1;
else return bfs(start);
}
};