HDU 1043 八数码问题的多种解法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 1043 八数码问题的多种解法相关的知识,希望对你有一定的参考价值。

一、思路很简单,搜索。对于每一种状态,利用康托展开编码成一个整数。于是,状态就可以记忆了。

二、在搜索之前,可以先做个优化,对于逆序数为奇数的序列,一定无解。

三、搜索方法有很多。

  1、最普通的:深搜、广搜。在这题里面,这两个方法直接TLE。所以,我后面没有贴超时的代码。

  2、既然1超时,那就预处理出所有状态,用map存储,然而,map的insert(使用[]是一样的)实在太慢了,也超时。

  3、在1的基础上,优化一下,得到:IDA*,双向广搜,A*。

  3、IDA*我没尝试,不过感觉没有A*快。另外,双向广搜可以加优化,在HDU上可以快大概300ms,就是:对于某一个方向,在开始BFS时,如果当前的队列里面状态个数很多,而另外一个队列里面相对较少,那么,把搜索方面换成另一边。这样,不至于出现两个队列的状态个数相差太远以至于退化成单向BFS的情况。当然,这个题两个队列中的状态都是比较均匀的,即使不加优化,效果也不会太差。

  4、不管用的是什么搜索,搜索过程中,多个样例经历经历相同的状态是非常有可能的,所以,理论上来说,可以加缓存,也就是其实不管是什么样例,只要序列确定,得到的整数编码一定是确定的,所以,理论上来说,加缓存可以加速,然而, 不知为何,这题,加了缓存还更慢。多半是因为我的缓存用的是C++ STL吧。

四、注意点

  1、双向BFS加优化时,C++的queue的size()的返回值类型是unsigned int,直接相减,会发生溢出的情况, 导致size()小的队列大小一直不变,size()大的队列一直在扩大,也就是退化成了单向BFS了。T_T。所以,为了防止这种情况发生,在size相减之前,用两个int变量先存储好这两个队列的size。否则,会很坑。

  2、在循环中开辟新指针,每一次循环开辟的指针值都一样,如代码所示。

#include<bits/stdc++.h>
using namespace std;

typedef struct Foo{
    int v1, v2;
    Foo(int _v1,int _v2){
        v1 = _v1;
        v2 = _v2;
    }
    Foo(){}
}Node;

set<Node*> s;
int main(){
    for(int i = 0;i < 5;++i){
        Node node = Node(i, i);
        s.insert(&node);
    }
    printf("%d\n", s.size());
}

输出结果会是1,而不是5。特别要注意。而如果这么写:

#include<bits/stdc++.h>
using namespace std;

typedef struct Foo{
    int v1, v2;
    Foo(int _v1,int _v2){
        v1 = _v1;
        v2 = _v2;
    }
    Foo(){}
}Node;

set<Node*> s;
int main(){
    for(int i = 0;i < 5;++i){
        Node* pnode = new Node(i, i);
        s.insert(pnode);
    }
    printf("%d\n", s.size());
}

因为C++没有自动垃圾回收,所以,很有可能会出现MLE的情况。因此,ACM竞赛中,尽可能地不要使用指针。

  3、C++的容器,map、string、vector之类的,慢!慢!慢!如果条件允许,时间又压得比较紧,特别是像string这种的,最好还是用C语言原生的字符串数组,虽然麻烦些,但是比string快不只一点点。

五、源代码

  1、双向BFS(队列容量均衡优化)

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int facts[15];

void init() {
    facts[0] = facts[1] = 1;
    for(int i = 2; i <= 10; ++i)facts[i] = facts[i - 1] * i;
}

int encode(vector<int>& seq) {
    int res = 0;
    int num = seq.size();
    for(int i = 0; i < num - 1; ++i) {
        int cnt = seq[i] - 1;
        for(int j = 0; j < i; ++j) {
            if(seq[j] < seq[i])--cnt;
        }
        res += cnt * facts[num - i - 1];
    }
    return res;
}

vector<int> temp;
void decode(int n, int m) {
    temp.clear();
    int buf = 0;
    int i, j, r, t;
    for(t = n; t > 0; --t) {
        r = m / facts[t - 1];
        m %= facts[t - 1];
        for(i = 1; i <= n; ++i) {
            if(!((buf >> i) & 1)) {
                if(r == 0)break;
                --r;
            }
        }
        temp.push_back(i);
        buf |= 1 << i;
    }
}

char line[30];
int get() {
    vector<int> vec;
    int len = strlen(line);
    for(int i = 0; i < len; ++i) {
        if(isdigit(line[i]))vec.push_back(line[i] - 0);
        else if(line[i] == x)vec.push_back(9);
    }
    int cnt = 0;
    for(int i = 0, sz = vec.size(); i < sz; ++i) {
        if(vec[i] == 9)continue;
        for(int j = 0; j < i; ++j) {
            if(vec[j] == 9)continue;
            if(vec[j] > vec[i])++cnt;
        }
    }
    if(cnt & 1)return -1;
    else return encode(vec);
}

int findPos() {
    for(int i = 0; i < 9; ++i)if(temp[i] == 9)return i;
    return -1;
}
typedef struct Foo {
    int code;
    char step;
    Foo(int l, char s) {
        code = l;
        step = s;
    }
    Foo() {}
} Node;
string res[2];
const int dirs[] = { -1, 1, -3, 3};
queue<int> que[2];
bool vis[2][362888];
Node pre[2][362888];
bool bfs(int s[]) {
    for(int i = 0; i < 2; ++i) {
        res[i].clear();
        while(!que[i].empty())que[i].pop();
        memset(vis[i], 0, sizeof(vis[i]));
        vis[i][s[i]] = true;
        for(int j = 0; j < 362888; ++j)pre[i][j].code = -1;
        que[i].push(s[i]);
    }
    int k = 0;
    while(!que[k].empty()) {
        int sz1 = que[k].size(), sz2 = que[k ^ 1].size();
        if(sz1 - sz2 > 1)k ^= 1;//队列均衡优化

        int code = que[k].front();
        que[k].pop();
        if(vis[k ^ 1][code]) {
            for(int i = 0; i < 2; ++i) {
                int now = code;
                int last = pre[i][now].code;
                while(last != -1) {
                    res[i].push_back(pre[i][now].step);
                    now = last;
                    last = pre[i][last].code;
                }
                if(i == 0)reverse(res[i].begin(), res[i].end());
            }
            return true;
        }
        decode(9, code);
        int pos = findPos();
        for(int i = 0; i < 4; ++i) {
            int np = pos + dirs[i];
            if(np < 0 || np > 8)continue;
            bool cond = false;
            if(i == 0 || i == 1)cond = (np / 3 == pos / 3);
            else if(i == 2 || i == 3)cond = (np >= 0 && np <= 8);
            if(cond) {
                swap(temp[np], temp[pos]);
                int nv = encode(temp);
                if(!vis[k][nv]) {
                    if(i == 0)pre[k][nv] = Node(code, l);
                    else if(i == 1)pre[k][nv] = Node(code, r);
                    else if(i == 2)pre[k][nv] = Node(code, u);
                    else if(i == 3)pre[k][nv] = Node(code, d);
                    que[k].push(nv);
                    vis[k][nv] = true;
                }
                swap(temp[np], temp[pos]);
            }
        }
        k ^= 1;
    }
    return false;
}

void change(string& s) {
    for(int i = 0, len = s.length(); i < len; ++i) {
        if(s[i] == l)s[i] = r;
        else if(s[i] == r)s[i] = l;
        else if(s[i] == u)s[i] = d;
        else if(s[i] == d)s[i] = u;
    }
    reverse(s.begin(), s.end());
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#endif // ONLINE_JUDGE
    ios::sync_with_stdio(false);
    init();
    while(gets(line) != NULL) {
        int v = get();
        if(v < 0)cout << "unsolvable" << endl;
        else if(v == 0)cout << endl;
        else {
            int s[] = {v, 0};
            if(bfs(s)) {
                change(res[1]);
                cout << res[0] << res[1] << endl;
            } else cout << "unsolvable" << endl;
        }
    }
}

2、A*搜索。

#include<bits/stdc++.h>
using namespace std;
int facts[10];
char input[30];
typedef struct Foo {
    int code;
    int pos9;
    double g, h;
    Foo(int c, int p, double _g, double _h) {
        code = c;
        pos9 = p;
        g = _g;
        h = _h;
    }
    Foo() {}
    /*这里写法决定时间的数量级:
        用注释的这种写法:耗时2700+ms;
        用没注释的这种写法,if括号里写成fabs(h - ano.h) < 1e-8,耗时1200+ms;
        现在这种写法:耗时800+ms;;
    */
    bool operator < (const Foo ano) const {
//        if(fabs(f - ano.f) < 1e-8)return h > ano.h;
//        else return f > ano.f;
        if(h == ano.h)return g > ano.g;
        else return h > ano.h;
    }
} Node;

void init0() {
    facts[0] = facts[1] = 1;
    for(int i = 2; i < 10; ++i)facts[i] = facts[i - 1] * i;
}

int encode(string& seq) {
    int res = 0, len = seq.length(), i, j, cnt;
    for(i = 0; i < len; ++i) {
        cnt = (seq[i] - 1);
        for(j = 0; j < i; ++j)if(seq[j] < seq[i])--cnt;
        res += cnt * facts[len - i - 1];
    }
    return res;
}

string decode(int code) {
    int i, j, r, t, board = 0;
    string res;
    for(t = 9; t > 0; --t) {
        r = code / facts[t - 1];
        code %= facts[t - 1];
        for(i = 1; i <= 9; ++i) {
            if(!((board >> i) & 1)) {
                if(r == 0)break;
                else --r;
            }
        }
        res.push_back(i + 0);
        board |= 1 << i;
    }
    return res;
}

double distance(int src) {
    double res = 0.0;
    string seq = decode(src);
    int len = seq.length();
    int i, j, x0, y0, x1, y1, num;
    for(i = 0; i < len; ++i) {
        num = seq[i] - 0;
        x0 = (num - 1) / 3, y0 = (num - 1) % 3;
        x1 = i / 3, y1 = i % 3;
        res += abs(x0 - x1) + abs(y0 - y1);
    }
    return res;
}

int find_pos9(string& str) {
    int len = str.length(), i;
    for(i = 0; i < len; ++i)if(str[i] == 9)return i;
    return -1;
}

const int dirs[] = { -1, 1, -3, 3};
priority_queue<Node> que;
bool vis[362888];
pair<int, char> pre[362888];
string ans;

bool bfs(int s) {
    int i, j, k;
    int pos9, npos9, ncode;
    string seq;
    Node top_node;
    while(!que.empty())que.pop();
    memset(vis, 0, sizeof(vis));
    for(i = 0; i < facts[9]; ++i)pre[i].first = -1;
    vis[s] = true;
    seq = decode(s);
    pos9 = find_pos9(seq);
    que.push(Node(s, pos9, 0, distance(s)));
    while(!que.empty()) {
        top_node = que.top();
        que.pop();
        if(top_node.code == 0) {
            ans.clear();
            for(int now = top_node.code, last = pre[top_node.code].first; last != -1; now = last, last = pre[last].first) {
                ans.push_back(pre[now].second);
            }
            reverse(ans.begin(), ans.end());
            return true;
        }
        seq = decode(top_node.code);
        pos9 = top_node.pos9;
        for(i = 0; i < 4; ++i) {
            npos9 = pos9 + dirs[i];
            if(npos9 < 0 || npos9 > 8)continue;
            if((i == 0 || i == 1) && npos9 / 3 != pos9 / 3)continue;
            if((i == 2 || i == 3) && (npos9 < 0 || npos9 > 8))continue;
            swap(seq[npos9], seq[pos9]);
            ncode = encode(seq);
            if(!vis[ncode]) {
                pre[ncode].first = top_node.code;
                if(i == 0)pre[ncode].second = l;
                else if(i == 1)pre[ncode].second = r;
                else if(i == 2)pre[ncode].second = u;
                else if(i == 3)pre[ncode].second = d;
                que.push(Node(ncode, npos9, top_node.g + 1, distance(ncode)));
                vis[ncode] = true;
            }
            swap(seq[npos9], seq[pos9]);
        }
    }
    return false;
}

int get_code(char str[]) {
    string temp;
    int len = strlen(str), i, j, cnt;
    for(i = 0; i < len; ++i) {
        if(isdigit(str[i]))temp.push_back(str[i]);
        else if(str[i] == x)temp.push_back(9);
    }
    cnt = 0;
    len = temp.length();
    for(i = 0; i < len; ++i) {
        if(temp[i] == 9)continue;
        for(j = 0; j < i; ++j) {
            if(temp[j] == 9)continue;
            if(temp[j] > temp[i])++cnt;

        }
    }
    if(cnt & 1)return -1;
    else return encode(temp);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif // ONLINE_JUDGE
    ios::sync_with_stdio(false);
    init0();
    while(gets(input) != NULL) {
        int code = get_code(input);
        if(code < 0)cout << "unsolvable" << endl;
        else if(code == 0)cout << endl;
        else cout << (bfs(code) ? ans : "unsolvable") << endl;
    }
    return 0;
}

3、暑假时候写的A*算法。耗时:400+ms。不知为何怎么这么快?Q_Q

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<queue>
#include<string>
#include<stack>
using namespace std;

typedef struct T {
    int nums[10];//使用1-9
    int ctv;//cantor value
    int index;//空格所在位置。
    double g, h;
    bool operator < (const T ano) const {
        if(h == ano.h)return g > ano.g;
        else return h > ano.h;
    }
} Node;

Node stnd;
const int facts[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
/*r,u,l,d*/
const int dirs[] = {1, -3, -1, 3};
const int N = 9;
const int dest_ctv = 1;
priority_queue<Node> que;
int vis[362888], pre[362888];

int en_cantor(int a[]);
void parse2int(char str[]);
bool has_solution();
double manhadun(Node n);
bool check(int index1, int index2);
void debug(Node n);

void bfs() {
    Node n2;
    while(!que.empty())que.pop();
    memset(vis, -1, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    que.push(stnd);
    vis[stnd.ctv] = -2;
    while(!que.empty()) {
        Node n = que.top();
        que.pop();
        for(int i = 0; i < 4; i++) {
            n2 = n;
            n2.index += dirs[i];
            if(!check(n2.index, n.index))continue;
            swap(n2.nums[n2.index], n2.nums[n.index]);
            n2.ctv = en_cantor(n2.nums);
            if(vis[n2.ctv] == -1) {
                n2.g++;
                n2.h = manhadun(n2);
                que.push(n2);
                vis[n2.ctv] = i;
                pre[n2.ctv] = n.ctv;
            }
            if(n2.ctv == dest_ctv)return;
        }
    }
}



void print() {
    stack<char> stk;
    for(int cur = dest_ctv; pre[cur] != -1; cur = pre[cur]) {
        if(vis[cur] == 0)stk.push(r);
        else if(vis[cur] == 1)stk.push(u);
        else if(vis[cur] == 2)stk.push(l);
        else if(vis[cur] == 3)stk.push(d);
    }
    while(!stk.empty()) {
        putchar(stk.top());
        stk.pop();
    }
    printf("\n");
}

int main() {
    //freopen("probDTDS.txt", "r", stdin);
    char input_data[40];
    while(gets(input_data) != NULL) {
        parse2int(input_data);
        if(!has_solution()) {
            printf("unsolvable\n");
            continue;
        } else {
            bfs();
            print();
        }
    }
    return 0;
}


int en_cantor(int a[]) {
    int res = 0, cnt = 0;
    const int n = N;
    for(int i = 1; i <= n; i++) {
        cnt = 0;
        for(int j = i + 1; j <= n; j++) {
            if(a[j] < a[i])cnt++;
        }
        res += cnt * facts[n - i];
    }
    return res + 1;
}

void parse2int(char str[]) {
    int len = strlen(str);
    int idx = 1;
    for(int i = 0; i < len; i++) {
        if(isdigit(str[i]))stnd.nums[idx++] = str[i] - 0;
        else if(str[i] == x) {
            stnd.nums[idx] = N;
            stnd.index = idx;
            idx++;
        }
    }
    stnd.ctv = en_cantor(stnd.nums);
}

bool has_solution() {
    int re_order = 0;
    for(int i = 1; i <= N; i++) {
        for(int j = 1; j < i; j++) {
            if(stnd.nums[i] != 9 && stnd.nums[j] != 9 &&
                    stnd.nums[i] < stnd.nums[j])re_order++;
        }
    }
    return re_order % 2 == 0;
}

double manhadun(Node n) {
    double res = 0;
    for(int i = 1; i <= N; i++) {
        if(n.nums[i] == 9)continue;
        int x = (n.nums[i] - 1) / 3, y = (n.nums[i] - 1) % 3;
        res += fabs((double)(x - (i - 1) / 3)) + fabs((double)(y - (i - 1) % 3));
    }
    return res;
}

bool check(int index1, int index2) {
    int mn = min(index1, index2);
    int mx = max(index1, index2);
    if(mn < 1 || mn > N || mx < 1 || mx > N)return false;
    if(mn == 1)return mx == 2 || mx == 4;
    else if(mn == 2)return mx == 3 || mx == 5;
    else if(mn == 3)return mx == 6;
    else if(mn == 4)return mx == 5 || mx == 7;
    else if(mn == 5)return mx == 6 || mx == 8;
    else if(mn == 6)return mx == 9;
    else if(mn == 7)return mx == 8;
    else if(mn == 8)return mx == 9;
    else return false;
}

void debug(Node n) {
    for(int i = 1; i <= N; i++)printf("%d ", n.nums[i]);
    printf("\n");
}

 

以上是关于HDU 1043 八数码问题的多种解法的主要内容,如果未能解决你的问题,请参考以下文章

搜索进阶1八数码(HDU1043)

Hdu 1043 Eight (八数码问题)

HDU 1043 Eight八数码解题思路(bfs+hash 打表 IDA* 等)

HDU 1043 Eight(八数码)

hdu 1043 Eight (八数码问题)BFS+康拓展开

八数码的A*与IDA*算法-搜索进阶练习1