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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章