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;
}