B - Eight II HDU - 3567
思路
- 这一题由于是多组输入,如果我们对每一组输入都进行一遍 bfs 这样肯定会T,
- 那么这一题的,奇妙思路就是预处理所有可能产生的终点状态,进行bfs逆向bfs
- 我们假设所以的逆向状态为:
“X12345678”,
“1X2345678”,
“12X345678”,
“123X45678”,
“1234X5678”,
“12345X678”,
“123456X78”,
“1234567X8”,
“12345678X”,
- 对于题目的中给我两个 起始、终点状态我们可以做对应的数字替换,过程为
- 设起始状态a:564178X23,
- 设终点状态b:7568X4123
- 我们把终点状态b 转化为上面9个状态中的一个(取决于X的位置):得到新的终点状态b’『1 2 3 4 X 5 6 7 8』—在这个转话过程重 7->1、5->2、6->3、8->3、X->X、4->5、1->6、2->7、3->8.
- 那么我们对起始状态a也相同的数字替换,就得到了新的起始状态a’ =『2 3 5 6 1 3 X 7 8』
- 接下来是预处理9个终点状态对它们分别跑一遍bfs,这样每一个终点状态会通过一层一层的搜索产生许多的 起点状态,
- 有了上面的预处理操作,对于每一组 始末状态 a、b我们对应转化为a’、b’,通过a’、b’ 在结合 预处理已经预处理时记录的路径,就能快速的得到ans了,,--------具体详细的还是看代码
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<string>
#include<cstdio>
#include<cmath>
#include<stack>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); } void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define m_p make_pair
#define INF 0x3f3f3f3f
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (s); i <= (e); i ++)
#define rep_(i, e, s) for(int i = (e); i >= (s); i --)
#define sd(a) scanf("%d", &a)
#define sc(a) scanf("%c", &a)
using namespace std;
struct Poi
{
char a[4][4];
Poi() {};
Poi(char s[])
{
for_(i, 0, 2)
for_(j, 0, 2)
a[i][j] = s[i * 3 + j];
}
};
char s[20], t[20];
int mk[9][500001]; //标记搜索的中的状态
vector<pair<int, char> > pa; //记录路径 <父节点索引,当前操作的符合>
int mov[4][2] = { 1, 0, 0, -1, 0, 1, -1, 0 };
int mul[9]; //提前预处理阶乘
char statu[9][20] = { //提前写出来我们要预转换成的9的个状态,要注意X的位置
"X12345678",
"1X2345678",
"12X345678",
"123X45678",
"1234X5678",
"12345X678",
"123456X78",
"1234567X8",
"12345678X",
};
char dir[5] = "dlru"; //因为是字典最小,所以搜索的 方向的先后顺序要注意
ll get_hash(Poi p)
{
int res = 0;
char te[20];
for_(i, 0, 2)
for_(j, 0, 2)
{
int cnt = 0;
te[i * 3 + j] = p.a[i][j];
for_(k, 0, i * 3 + j - 1)
if(te[k] > te[i * 3 + j])
cnt ++;
res += cnt * mul[i * 3 + j];
}
return res;
}
void bfs(int k) //以终点状态statu[k]数组,来逆向搜索的得出的 从终点状态能演变出的所有的其它状态(这的状态指的是的有 1~8、和X组成的序列)状态
{
Poi s(statu[k]);
queue<pair<Poi, pair<int, int> > > q; //<状态,<父节点索引,X的位置>
q.push(m_p(s, m_p(-1, k)));
mk[k][get_hash(s)] = -1; //对起点做一下标记
while(! q.empty())
{
pair<Poi, pair<int, int> > t = q.front(); q.pop();
int x = t.se.se / 3, y = t.se.se % 3;
for_(i, 0, 3)
{
int xx = x + mov[i][0];
int yy = y + mov[i][1];
if(xx < 0 || yy < 0 || xx >= 3 || yy >= 3) continue;
swap(t.fi.a[x][y], t.fi.a[xx][yy]);
if(! mk[k][get_hash(t.fi)])
{
mk[k][get_hash(t.fi)] = pa.size(); //这里我们千万要注意:mk 里面的存储的是 pa的尺寸,有pa容器的尺寸是不断的变大的,所以我们可以把它作为一个唯一的 标识下标(这个标识 在后面作为 路径记录时的下标来用)
q.push(m_p(t.first, m_p(pa.size(), xx * 3 + yy)));
pa.pb(m_p(t.se.fi, dir[i])); //pa的容器尺寸++
}
swap(t.fi.a[x][y], t.fi.a[xx][yy]); //还原回来原来的改变
}
}
}
void sol()
{
int num[300], p = 0;
for(int i = 0, j = 0; i < 9; i ++) //我们将b数组转化成 某个标准状态数组 statu(预处理成哪一个取决于X的位置)
if(s[i] == 'X') p = i; //记录p记录X的位置
else num[s[i]] = ++ j;
for_(i, 0, 8) if(t[i] != 'X') t[i] = num[t[i]] + '0'; //相应的数字进行对应转化
int hs = mk[p][get_hash(t)]; //我们考虑p的意思 和 mk 存储的是什么?
vector<char> ans; //记录ans
while(hs != -1)
{
ans.pb(pa[hs].se);
hs = pa[hs].fi;
}
printf("%lu\n", ans.size());
rep_(i, ans.size() - 1, 0)
printf("%c", ans[i]);
printf("\n");
}
int main()
{
/* fre(); */
mul[0] = 1;
for_(i, 1, 9) mul[i] = mul[i - 1] * i; //预处理阶乘
for_(i, 0, 8) bfs(i); //预处理 每一个可能的终点状态statu[i],到其它状态的转换
int T, Case = 1;
sd(T);
while(T --)
{
scanf("%s %s", s, t);
printf("Case %d: ", Case ++);
sol();
}
return 0;
}