Eight
解題思路
- 因為終點狀態是固定的,而從終點到起點是可以走到的,那麼起點到終點也是可以走到的,因為它是一個無向圖
- 那麼我們可以把所有可以到達的狀態存起來,而到達不了的輸出不可能
- 那麼我們就可以一次初始化,就得到所以情況了
程式碼實現
#include <sstream>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<queue>
#define Maxn 362880+5//876543210的hash值為362880 即最多出現362880種可能
using namespace std;
static const int FAC[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 }; // 階乘
//佇列中存放的資訊
struct p {
char s[10];//表示當前圖的狀態
int hash;//當前圖狀態的康拓值
int index;//空格所在的位置
};
bool v[Maxn];//用來標記可以到達的狀態
string path[Maxn];//存放從終點到一種狀態的路徑
int result = 46234;//123456780,終點狀態的康拓值
p q[Maxn];//佇列
int l = 0;//佇列頭
int r = 0;//佇列尾
p s = { "123456780", result, 8 };//開始位置
p t;
int d[4][2] = {//四個方向
{-1, 0}, {1, 0}, {0, -1}, {0, 1}
};
//康託展開
int cantor(char* a)
{
int x = 0;
for (int i = 0; i < 9; ++i)
{
int smaller = 0; // 在當前位之後小於其的個數
for (int j = i + 1; j < 9; ++j)
{
if (a[j] < a[i])
smaller++;
}
x += FAC[9 - i - 1] * smaller; // 康託展開累加
}
return x + 1; // 康託展開值
}
void bfs()
{
v[s.hash] = true;//標記當前狀態可以到達
path[s.hash] = "";//終點到當前狀態的路徑為NULL
q[r++] = s;//放入佇列
while (l < r) {//佇列不為空
s = q[l++];//彈出佇列元素
int x = s.index / 3;//計算在圖中的下標
int y = s.index % 3;
for (int i = 0; i < 4; i++) {//列舉四個方向
int nx = x + d[i][0];//要交換的數的位置
int ny = y + d[i][1];
if (nx >= 0 && ny >= 0 && nx < 3 && ny < 3) {//沒有越界
t = s;//複製一份方便操作
swap(t.s[nx * 3 + ny], t.s[t.index]);//模擬移動過程
int newHash = cantor(t.s);//計算改變之後的康拓值
if (!v[newHash]) {//如果沒有到達過這種狀態
t.hash = newHash;//更新康拓值
t.index = nx * 3 + ny;//更新空格的位置
switch (i) {//判斷是哪一個操作
case 0: {
path[t.hash] = path[s.hash] + "d";//因為是從終點到起點所以是反過來的操作
break;
}
case 1: {
path[t.hash] = path[s.hash] + "u";
break;
}
case 2: {
path[t.hash] = path[s.hash] + "r";
break;
}
case 3: {
path[t.hash] = path[s.hash] + "l";
break;
}
}
q[r++] = t;//入隊
v[newHash] = true;//標記這種狀態
}
}
}
}
}
int main() {
bfs();
string a;
while (getline(cin, a)) {
if (a.size() == 0) {
break;
}
char s[10] = "";
int size = 0;
for (int i = 0; i < a.size(); i++) {
if (a[i] != ' ') {
if (a[i] != 'x') {
s[size++] = a[i];
}
else {
s[size++] = '0';
}
}
}
int hash = cantor(s);//圖的狀態用康拓值來表示
if (!v[hash]) {//為0代表不能到達
cout << "unsolvable" << endl;
}
else {
reverse(path[hash].begin(), path[hash].end());//因為是起點到終點所以需要翻轉,不然過不了
cout << path[hash] << endl;
}
}
return 0;
}
勝利大逃亡(續)
解題思路
- 分層圖最短路演算法
- 不把圖中的節點當做bfs的節點,而把圖中的節點加上題目要求的狀態當做我們bfs的節點
- 下面就是bfs板子
程式碼實現
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
const int N = 21;
struct p {
int x;
int y;
int status;
};
int n;
int m;
int t;
char map[N][N];
p s;
p q[N * N * N * N * N];
int sx;
int sy;
int d[4][2] = {
{-1, 0}, {1, 0}, {0, -1}, {0, 1}
};
bool v[N][N][1 << 11 + 1] = { 0 };
void solve()
{
memset(v, 0, sizeof(v));
int level = 0;
int l = 0;
int r = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> map[i][j];
if (map[i][j] == '@') {
sx = i;
sy = j;
}
}
}
v[sx][sy][0] = 1;
s.x = sx;
s.y = sy;
s.status = 0;
q[r++] = s;
while (l < r) {
int size = r - l;
if (level >= t) {
cout << -1 << endl;
return;
}
for (int i = 0; i < size; i++) {
s = q[l++];
if (map[s.x][s.y] == '^' && level < t) {
cout << level << endl;
return;
}
for (int j = 0; j < 4; j++) {
int nx = s.x + d[j][0];
int ny = s.y + d[j][1];
int ns = s.status;
if (nx < 0 || nx >= n || ny < 0 || ny >= m || map[nx][ny] == '*') {
continue;
}
if (map[nx][ny] >= 'a' && map[nx][ny] <= 'j' && !v[nx][ny][ns]) {
ns |= 1 << (map[nx][ny] - 'a');
v[nx][ny][ns] = 1;
q[r].x = nx;
q[r].y = ny;
q[r++].status = ns;
}
else if (map[nx][ny] >= 'A' && map[nx][ny] <= 'J') {
if ((ns >> (map[nx][ny] - 'A') & 1) && !v[nx][ny][ns]) {
v[nx][ny][ns] = 1;
q[r].x = nx;
q[r].y = ny;
q[r++].status = ns;
}
}
else {
if (!v[nx][ny][ns]) {
v[nx][ny][ns] = 1;
q[r].x = nx;
q[r].y = ny;
q[r++].status = ns;
}
}
}
}
level++;
}
cout << -1 << endl;
}
int main()
{
while (~scanf("%d%d%d", &n, &m, &t)) {
solve();
}
return 0;
}