2024年3月25号题解
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;
}