A - Eight HDU - 1043(A*算法、雙向bfs)

A - Eight HDU - 1043(A*算法、雙向bfs)

題意

  1. 給我們 8個數(1,23…)和一個字母x組成的一個序列,讓我們通過 上下左右移動x操作,把這個序列變成:1 2 3 4 5 6 7 8 x

思路一

  • 雙向bfs
  1. 我們先考慮,什麼是 雙向bfs ?,簡單的說就是,從起點、終點兩個方向開始 bfs搜索,
  2. 這個算法的實現是通過
    1. 兩個隊列 q1q2q1、q2, 我們分別把起點、終點的搜索狀態 壓入q1q2q1、q2,
    2. 再通過 vis1[ ][ ]vis2[ ][ ]~ vis1[~][~]、vis2[~][~] 數組分別 標記 q1 所代表的正向bfs搜索中 所走過的點、標記 q1 所代表的正向bfs搜索中 所走過的點。
    3. 我們進行具體搜索的時候(就是再代碼實現中while(q1 != emtpy && q2 != empty)的內部),我們分別從 q1、q2中各取出一個元素(設爲a、b),先對第一個元素a進行bfs搜索,如果在搜索到某個位置、狀態的時候,而這個位置、狀態已經在vis2[][]中出現了,那麼就完成了搜索任務;否則的再進行都b的bfs搜索若果它再bfs搜索過程中到的某個位置、狀態再vis1[][]中出現過的話,那麼ok也完成了搜索的任務,可以輸出ans了,,,如果沒有的話,就再從q1、q2中再各取一個元素,再進行搜索,,,這樣一個循環下去就能得到ans。。
    4. 還有要注意細節,兩個bfs循環中的 ,第一個bfs搜索的 位置改變是正着的,而第二個bfs 位置改變我們 記錄成 逆向的 (如x—>y,我在記錄路徑的時候:記錄成 y–>x)
  3. 具體的到這個題中要進行的,
    1. 首先我們要考慮怎麼記錄每一個搜索狀態,這裏要用到 康託展開,他是幹什麼的,就是對於每一個 序列(元素不重複),我們可以 給他一個 編號,而這個『編號』,我們可以通過 vis[編號] = 1 來記錄已經搜索過的狀態,
    2. 記錄搜索的路徑,這裏我們通過 並查集 來記錄的搜的路徑,在記錄路徑的時候,我們一定要保證,路徑所代表的下表是唯一的(不能用康託展開對每個狀態產生的編號),具體實現在 代碼中 多注意!!!
  4. 我們還提前判斷的 剛開始所給序列的逆序數的數量(設爲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()
{
    /* fre(); */
    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;
}

思路二

  1. 對這個的算法我的理解:首先不要把這個算法看的太難(其實思路很簡單)
    1. 我們一開始可以把他當成bfs搜索,不過這個搜的隊列 是priority_queue 來實現的,既然是優先級的列的那就要 按照的優先級的 高低 來決定的搜索的順序,
    2. 那麼對於任意一個搜索位置、狀態 我們怎麼確定的它的優先級的大小呢?這裏我們通過引入 估值函數 f=g+h優先級f = g + h,在迷宮問題中g爲從最初的搜索的狀態(起點)到當前正在討論的搜索狀態,走了多少步/用了多少時間;h爲桶當前正在討論的搜索狀態->到目標搜索的狀態(終點)所需要的 步數/時間;g+h就得到了 每個狀態(正在搜索的位置)的優先級 f,通過f 就可在 priority_queue 中自動排序了,按優先級高(這裏的優先級高指的是 f值越小)的搜索了,這樣就可 一直搜索就能的得到的ans了,
      注意上面的 步數/時間 在A* 算法中一般指的是 曼哈頓距離(橫、縱座標的差的絕對值之和)
  2. 對於這一題,記錄狀態 時的下標我們可以直接中 康託展開產生的編號 坐下標(注意:爲什的在這個算法中 可以 用它做下標,而在第一個中卻不可以)
  3. 記錄路 還是用並查集
  4. 還要判斷 初始序列的數的數量
#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;                   //p爲x 的座標
    int g, h;                //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()
{
    /* fre(); */
    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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章