A - Eight HDU - 1043(A*算法、雙向bfs)
題意
- 給我們 8個數(1,23…)和一個字母x組成的一個序列,讓我們通過 上下左右移動x操作,把這個序列變成:1 2 3 4 5 6 7 8 x
思路一
- 我們先考慮,什麼是 雙向bfs ?,簡單的說就是,從起點、終點兩個方向開始 bfs搜索,
- 這個算法的實現是通過
- 兩個隊列 q1、q2, 我們分別把起點、終點的搜索狀態 壓入q1、q2,
- 再通過 vis1[ ][ ]、vis2[ ][ ] 數組分別 標記 q1 所代表的正向bfs搜索中 所走過的點、標記 q1 所代表的正向bfs搜索中 所走過的點。
- 我們進行具體搜索的時候(就是再代碼實現中while(q1 != emtpy && q2 != empty)的內部),我們分別從 q1、q2中各取出一個元素(設爲a、b),先對第一個元素a進行bfs搜索,如果在搜索到某個位置、狀態的時候,而這個位置、狀態已經在vis2[][]中出現了,那麼就完成了搜索任務;否則的再進行都b的bfs搜索若果它再bfs搜索過程中到的某個位置、狀態再vis1[][]中出現過的話,那麼ok也完成了搜索的任務,可以輸出ans了,,,如果沒有的話,就再從q1、q2中再各取一個元素,再進行搜索,,,這樣一個循環下去就能得到ans。。
- 還有要注意細節,兩個bfs循環中的 ,第一個bfs搜索的 位置改變是正着的,而第二個bfs 位置改變我們 記錄成 逆向的 (如x—>y,我在記錄路徑的時候:記錄成 y–>x)
- 具體的到這個題中要進行的,
- 首先我們要考慮怎麼記錄每一個
搜索狀態
,這裏要用到 康託展開,他是幹什麼的,就是對於每一個 序列(元素不重複),我們可以 給他一個 編號,而這個『編號』,我們可以通過 vis[編號] = 1 來記錄已經搜索過的狀態,
- 記錄搜索的路徑,這裏我們通過 並查集 來記錄的搜的路徑,在記錄路徑的時候,我們一定要保證,路徑所代表的下表是唯一的(不能用康託展開對每個狀態產生的編號),具體實現在 代碼中 多注意!!!
- 我們還提前判斷的 剛開始所給序列的逆序數的數量(設爲ct),如果 ct 爲偶數則有可能有ans;否則一定沒有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 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;
const int N = 10;
const int mxn = 5e5 + 10;
int mov[4] = { -3, 3, -1, 1 };
int mul[N] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int vis1[mxn];
int vis2[mxn];
char d1[5] = "udlr";
char d2[5] = "durl";
struct Node
{
int a[N];
int p;
} S1, S2;
struct Path
{
int num;
char ch;
} ph[mxn];
int contor(int a[N])
{
int res = 0;
for_(i, 1, 9)
{
int ct = 0;
for_(j, 1, i - 1)
if(a[i] < a[j])
ct ++;
res += ct * mul[i - 1];
}
return res;
}
void print(int x)
{
if(ph[x].num == -1) return;
print(ph[x].num);
printf("%c", ph[x].ch);
}
bool good(int i, int p)
{
if(i == 0 && p < 4) return false;
if(i == 1 && p > 6) return false;
if(i == 2 && p % 3 == 1) return false;
if(i == 3 && p % 3 == 0) return false;
return true;
}
void bfs()
{
ph[1].num = -1;
vis1[contor(S1.a)] = 1;
ph[2].num = -1;
vis2[contor(S2.a)] = 2;
queue<Node> q1, q2;
q1.push(S1);
q2.push(S2);
int n = 2;
while(! q1.empty() && ! q2.empty())
{
Node T1 = q1.front(); q1.pop();
int ct_pre = contor(T1.a);
if(vis2[ct_pre])
{
print(vis1[ct_pre]);
int x = vis2[ct_pre];
while(ph[x].num != -1)
{
printf("%c", ph[x].ch);
x = ph[x].num;
}
printf("\n");
return;
}
for_(i, 0, 3)
{
Node now = T1;
if(! good(i, now.p)) continue;
swap(now.a[now.p], now.a[now.p + mov[i]]);
now.p += mov[i];
int ct_now = contor(now.a);
if(vis1[ct_now]) continue;
vis1[ct_now] = ++ n;
ph[n].num = vis1[ct_pre];
ph[n].ch = d1[i];
q1.push(now);
}
Node T2 = q2.front(); q2.pop();
ct_pre = contor(T2.a);
if(vis1[ct_pre])
{
print(vis1[ct_pre]);
int x = vis2[ct_pre];
while(ph[x].num != -1)
{
printf("%c", ph[x].ch);
x = ph[x].num;
}
printf("\n");
return;
}
for_(i, 0, 3)
{
Node now = T2;
if(! good(i, now.p)) continue;
swap(now.a[now.p], now.a[now.p + mov[i]]);
now.p += mov[i];
int ct_now = contor(now.a);
if(vis2[ct_now]) continue;
vis2[ct_now] = ++ n;
ph[n].num = vis2[ct_pre];
ph[n].ch = d2[i];
q2.push(now);
}
}
printf("unsolvable\n");
}
void init()
{
for_(i, 1, 9) S2.a[i] = i % 9;
S2.p = 9;
}
int main()
{
char ch;
init();
while(sc(ch) != EOF)
{
for_(i, 1, 9)
{
if(i != 1) sc(ch);
if(ch == 'x')
S1.a[i] = 0, S1.p = i;
else
S1.a[i] = ch - '0';
getchar();
}
int k = 0;
for_(i, 1, 9)
{
if(S1.a[i] == 0) continue;
for_(j, i + 1, 9)
{
if(S1.a[j] == 0) continue;
if(S1.a[i] > S1.a[j]) k ++;
}
}
memset(vis1, 0, sizeof(vis1));
memset(vis2, 0, sizeof(vis2));
if(k & 1) printf("unsolvable\n");
else bfs();
}
return 0;
}
思路二
- 對這個的算法我的理解:首先不要把這個算法看的太難(其實思路很簡單)
- 我們一開始可以把他當成bfs搜索,不過這個搜的隊列 是priority_queue 來實現的,既然是優先級的列的那就要 按照的優先級的 高低 來決定的搜索的順序,
- 那麼對於任意一個搜索位置、狀態 我們怎麼確定的它的優先級的大小呢?這裏我們通過引入 估值函數 優先級f=g+h,在迷宮問題中g爲從最初的搜索的狀態(起點)到當前正在討論的搜索狀態,走了多少步/用了多少時間;h爲桶當前正在討論的搜索狀態->到目標搜索的狀態(終點)所需要的 步數/時間;g+h就得到了 每個狀態(正在搜索的位置)的優先級 f,通過f 就可在 priority_queue 中自動排序了,按優先級高(這裏的優先級高指的是 f值越小)的搜索了,這樣就可 一直搜索就能的得到的ans了,
注意上面的 步數/時間 在A* 算法中一般指的是 曼哈頓距離(橫、縱座標的差的絕對值之和)
- 對於這一題,記錄狀態 時的下標我們可以直接中 康託展開產生的編號 坐下標(注意:爲什的在這個算法中 可以 用它做下標,而在第一個中卻不可以)
- 記錄路 還是用並查集
- 還要判斷 初始序列的數的數量
#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 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;
const int N = 10;
const int mxn = 5e5 + 10;
int mov[4] = { -3, 3, -1, 1 };
int mul[N] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
char dir[N] = "udlr";
int vis[mxn];
Pir ps[N];
struct Node
{
int a[N];
int p;
int g, h;
int idx;
bool operator < (const Node x) const
{
return h + g > x.h + x.g;
}
} s;
struct Path
{
int idx;
char ch;
} ph[mxn];
void print(int x)
{
if(ph[x].idx == -1) return;
print(ph[x].idx);
printf("%c", ph[x].ch);
}
bool good(int i, int p)
{
if(i == 0 && p < 4) return false;
if(i == 1 && p > 6) return false;
if(i == 2 && p % 3 == 1) return false;
if(i == 3 && p % 3 == 0) return false;
return true;
}
int contor(int a[N])
{
int res = 0;
for_(i, 1, 9)
{
int ct = 0;
for_(j, 1, i - 1)
if(a[i] < a[j])
ct ++;
res += ct * mul[i - 1];
}
return res;
}
int get_h(int a[N])
{
int h = 0;
for_(i, 0, 2)
for_(j, 1, 3)
h += abs(ps[a[i * 3 + j]].fi - (i + 1)) + abs(ps[a[i * 3 + j]].se - j);
return h;
}
void A_star()
{
memset(vis, 0, sizeof(vis));
int ct = contor(s.a);
vis[ct] = 1;
ph[ct].idx = -1;
s.idx = ct;
s.g = 0;
s.h = get_h(s.a);
priority_queue<Node> q;
q.push(s);
while(! q.empty())
{
s = q.top(); q.pop();
if(s.h == 0)
{
print(s.idx);
printf("\n");
return;
}
for_(i, 0, 3)
{
Node t = s;
if(! good(i, t.p)) continue;
swap(t.a[t.p], t.a[t.p + mov[i]]);
t.idx = contor(t.a);
if(vis[t.idx]) continue;
vis[t.idx] = 1;
t.p += mov[i];
t.g ++;
t.h = get_h(t.a);
ph[t.idx].idx = s.idx;
ph[t.idx].ch = dir[i];
q.push(t);
}
}
printf("unsolvable\n");
}
int main()
{
ps[1].fi = 1, ps[1].se = 1;
ps[2].fi = 1, ps[2].se = 2;
ps[3].fi = 1, ps[3].se = 3;
ps[4].fi = 2, ps[4].se = 1;
ps[5].fi = 2, ps[5].se = 2;
ps[6].fi = 2, ps[6].se = 3;
ps[7].fi = 3, ps[7].se = 1;
ps[8].fi = 3, ps[8].se = 2;
ps[0].fi = 3, ps[0].se = 3;
char ch;
while(sc(ch) != EOF)
{
for_(i, 1, 9)
{
if(i != 1) sc(ch);
if(ch == 'x')
s.a[i] = 0, s.p = i;
else
s.a[i] = ch - '0';
getchar();
}
int k = 0;
for_(i, 1, 9)
{
if(s.a[i] == 0) continue;
for_(j, i + 1, 9)
{
if(s.a[j] == 0) continue;
if(s.a[i] > s.a[j]) k ++;
}
}
if(k & 1) printf("unsolvable\n");
else A_star();
}
return 0;
}