zoukankan      html  css  js  c++  java
  • 八数码 poj 1077 广搜 A* IDA* yongmou

    经典的八数码问题,有人说不做此题人生不完整,哈哈。

    状态总数是9! = 362880 种,不算太多,可以满足广搜和A*对于空间的需求。

    状态可以每次都动态生成,也可以生成一次存储起来,我用的动态生成,《组合数学》书上有一种生成排列的方法叫做"序数法",我看了一会书,把由排列到序数,和由序数到排列的两个函数写了出来,就是代码中的int order(const char *s, int n) 和void get_node(int num, node &tmp)两个函数。

    启发函数,用的是除空格外的八个数字到正确位置的网格距离。

    几种方法的比较:广搜,效率最低,500ms;A*,32ms,已经比较高效了;IDA*, 0ms,空间也减少许多。A*为判重付出了巨大代价,时间 and 空间,uva上还有一个15数码的题,用A*肯定会爆空间的。IDA*不记录已经走过的路径,所以省去了空间,也省去了判断重复的步骤,但是会出现重复计算。

    广搜:

    代码
    // BFS
    #include<iostream>
    #include
    <cstdio>
    #include
    <queue>
    using namespace std;

    /* 把1..n的排列映射为数字 0..(n!-1) */
    int fac[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };//...
    int order(const char *s, int n) {
    int i, j, temp, num;

    num
    = 0;

    for (i = 0; i < n-1; i++) {
    temp
    = 0;
    for (j = i + 1; j < n; j++) {
    if (s[j] < s[i])
    temp
    ++;
    }
    num
    += fac[s[i] -1] * temp;
    }
    return num;
    }

    bool is_equal(const char *b1, const char *b2){
    for(int i=0; i<9; i++)
    if(b1[i] != b2[i])
    return false;
    return true;
    }


    //hash
    struct node{
    char board[9];
    char space;//空格所在位置
    };

    const int TABLE_SIZE = 362880;

    int hash(const char *cur){
    return order(cur, 9);
    }

    /* 整数映射成排列 */
    void get_node(int num, node &tmp) {
    int n=9;
    int a[9]; //求逆序数
    for (int i = 2; i <= n; ++i) {
    a[i
    - 1] = num % i;
    num
    = num / i;
    tmp.board[i
    - 1] = 0;//初始化
    }
    tmp.board[
    0] = 0;
    int rn, i;
    for (int k = n; k >= 2; k--) {
    rn
    = 0;
    for (i = n - 1; i >= 0; --i) {
    if (tmp.board[i] != 0)
    continue;
    if (rn == a[k - 1])
    break;
    ++rn;
    }
    tmp.board[i]
    = k;
    }
    for (i = 0; i < n; ++i)
    if (tmp.board[i] == 0) {
    tmp.board[i]
    = 1;
    break;
    }
    tmp.space
    = n - a[n-1] -1;
    }

    char visited[TABLE_SIZE];
    int parent[TABLE_SIZE];
    char move[TABLE_SIZE];
    int step[4][2] = {{-1, 0},{1, 0}, {0, -1}, {0, 1}};//u, d, l, r

    void BFS(const node & start){
    int x, y, k, a, b;
    int u, v;

    for(k=0; k<TABLE_SIZE; ++k)
    visited[k]
    = 0;
    u
    = hash(start.board);
    parent[u]
    = -1;
    visited[u]
    = 1;

    queue
    <int> que;
    que.push(u);

    node tmp, cur;
    while(!que.empty()){
    u
    = que.front();
    que.pop();

    get_node(u, cur);

    k
    = cur.space;
    x
    = k / 3;
    y
    = k % 3;
    for(int i=0; i<4; ++i){
    a
    = x + step[i][0];
    b
    = y + step[i][1];
    if(0<=a && a<=2 && 0<=b && b<=2){
    tmp
    = cur;
    tmp.space
    = a*3 + b;
    swap(tmp.board[k], tmp.board[tmp.space]);
    v
    = hash(tmp.board);
    if(visited[v] != 1){
    move[v]
    = i;
    visited[v]
    = 1;
    parent[v]
    = u;
    if(v == 0) //目标结点hash值为0
    return;

    que.push(v);
    }
    }
    }
    }
    }

    void print_path(){
    int n, u;
    char path[1000];
    n
    = 1;
    path[
    0] = move[0];
    u
    = parent[0];
    while(parent[u] != -1){
    path[n]
    = move[u];
    ++n;
    u
    = parent[u];
    }
    for(int i=n-1; i>=0; --i){
    if(path[i] == 0)
    printf(
    "u");
    else if(path[i] == 1)
    printf(
    "d");
    else if(path[i] == 2)
    printf(
    "l");
    else
    printf(
    "r");
    }
    }

    int main(){
    freopen(
    "in", "r", stdin);

    node start;
    char c;
    for(int i=0; i<9; ++i){
    cin
    >>c;
    if(c == 'x'){
    start.board[i]
    = 9;
    start.space
    = i;
    }
    else
    start.board[i]
    = c - '0';
    }
    BFS(start);

    if(visited[0] == 1)
    print_path();
    else
    printf(
    "unsolvable");
    return 0;
    }

    A*算法:

    代码中对priority_queue<>模板的使用还是很有技巧性的,通过push一个最小的,再把它pop出来就解决了由于更改造成不一致性。前面这种做法是错误的,多谢一位朋友的提醒,这种方式确实不能保持堆的性质。不过我们可以采用冗余的办法,直接插入新值,这就不会破坏堆的性质了。

    修改过的代码:

    代码
    // A*
    #include<iostream>
    #include
    <cstdio>
    #include
    <cstring>
    #include
    <cstdlib>
    #include
    <queue>
    using namespace std;

    /* 把1..n的排列映射为数字 0..(n!-1) */
    int fac[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };//...
    int order(const char *s, int n) {
    int i, j, temp, num;

    num
    = 0;

    for (i = 0; i < n-1; i++) {
    temp
    = 0;
    for (j = i + 1; j < n; j++) {
    if (s[j] < s[i])
    temp
    ++;
    }
    num
    += fac[s[i] -1] * temp;
    }
    return num;
    }

    bool is_equal(const char *b1, const char *b2){
    for(int i=0; i<9; i++)
    if(b1[i] != b2[i])
    return false;
    return true;
    }


    //hash
    struct node{
    char board[9];
    char space;//空格所在位置
    };

    const int TABLE_SIZE = 362880;

    int hash(const char *cur){
    return order(cur, 9);
    }

    /* 整数映射成排列 */
    void get_node(int num, node &tmp) {
    int n=9;
    int a[9]; //求逆序数
    for (int i = 2; i <= n; ++i) {
    a[i
    - 1] = num % i;
    num
    = num / i;
    tmp.board[i
    - 1] = 0;//初始化
    }
    tmp.board[
    0] = 0;
    int rn, i;
    for (int k = n; k >= 2; k--) {
    rn
    = 0;
    for (i = n - 1; i >= 0; --i) {
    if (tmp.board[i] != 0)
    continue;
    if (rn == a[k - 1])
    break;
    ++rn;
    }
    tmp.board[i]
    = k;
    }
    for (i = 0; i < n; ++i)
    if (tmp.board[i] == 0) {
    tmp.board[i]
    = 1;
    break;
    }
    tmp.space
    = n - a[n-1] -1;
    }

    //启发函数: 除去x之外到目标的网格距离和
    int goal_state[9][2] = {{0,0}, {0,1}, {0,2},
    {
    1,0}, {1,1}, {1,2}, {2,0}, {2,1}, {2,2}};
    int h(const char *board){
    int k;
    int hv = 0;
    for(int i=0; i<3; ++i)
    for(int j=0; j<3; ++j){
    k
    = i*3+j;
    if(board[k] != 9){
    hv
    += abs(i - goal_state[board[k]-1][0]) +
    abs(j
    - goal_state[board[k] -1][1]);
    }
    }
    return hv;
    }

    int f[TABLE_SIZE], d[TABLE_SIZE];//估计函数和深度

    //优先队列的比较对象
    struct cmp{
    bool operator () (int u, int v){
    return f[u] > f[v];
    }
    };
    char color[TABLE_SIZE];//0, 未访问;1, 在队列中,2, closed
    int parent[TABLE_SIZE];
    char move[TABLE_SIZE];
    int step[4][2] = {{-1, 0},{1, 0}, {0, -1}, {0, 1}};//u, d, l, r

    void A_star(const node & start){
    int x, y, k, a, b;
    int u, v;
    priority_queue
    <int, vector<int>, cmp> open;
    memset(color,
    0, sizeof(char) * TABLE_SIZE);

    u
    = hash(start.board);
    parent[u]
    = -1;
    d[u]
    = 0;
    f[u]
    = h(start.board);
    open.push(u);
    color[u]
    = 1;

    node tmp, cur;
    while(!open.empty()){
    u
    = open.top();
    if(u == 0)
    return;
    open.pop();

    get_node(u, cur);

    k
    = cur.space;
    x
    = k / 3;
    y
    = k % 3;
    for(int i=0; i<4; ++i){
    a
    = x + step[i][0];
    b
    = y + step[i][1];
    if(0<=a && a<=2 && 0<=b && b<=2){
    tmp
    = cur;
    tmp.space
    = a*3 + b;
    swap(tmp.board[k], tmp.board[tmp.space]);
    v
    = hash(tmp.board);
    if(color[v] == 1 && (d[u] + 1) < d[v]){//v in open
    move[v] = i;
    f[v]
    = f[v] - d[v] + d[u] + 1;//h[v]已经求过
    d[v] = d[u] + 1;
    parent[v]
    = u;
    //直接插入新值, 有冗余,但不会错
    open.push(v);
    }
    else if(color[v] == 2 && (d[u]+1)<d[v]){//v in closed
    move[v] = i;
    f[v]
    = f[v] - d[v] + d[u] + 1;//h[v]已经求过
    d[v] = d[u] + 1;
    parent[v]
    = u;
    open.push(v);
    color[v]
    = 1;
    }
    else if(color[v] == 0){
    move[v]
    = i;
    d[v]
    = d[u] + 1;
    f[v]
    = d[v] + h(tmp.board);
    parent[v]
    = u;
    open.push(v);
    color[v]
    = 1;
    }
    }
    }
    color[u]
    = 2; //
    }
    }

    void print_path(){
    int n, u;
    char path[1000];
    n
    = 1;
    path[
    0] = move[0];
    u
    = parent[0];
    while(parent[u] != -1){
    path[n]
    = move[u];
    ++n;
    u
    = parent[u];
    }
    for(int i=n-1; i>=0; --i){
    if(path[i] == 0)
    printf(
    "u");
    else if(path[i] == 1)
    printf(
    "d");
    else if(path[i] == 2)
    printf(
    "l");
    else
    printf(
    "r");
    }
    }

    int main(){
    //freopen("in", "r", stdin);

    node start;
    char c;
    for(int i=0; i<9; ++i){
    cin
    >>c;
    if(c == 'x'){
    start.board[i]
    = 9;
    start.space
    = i;
    }
    else
    start.board[i]
    = c - '0';
    }
    A_star(start);

    if(color[0] != 0)
    print_path();
    else
    printf(
    "unsolvable");
    return 0;
    }

    IDA*:
    代码
    // IDA*
    #include<iostream>
    #include
    <cstdio>
    #include
    <cstdlib>
    using namespace std;

    #define SIZE 3

    char board[SIZE][SIZE];

    //启发函数: 除去x之外到目标的网格距离和
    int goal_state[9][2] = {{0,0}, {0,1}, {0,2},
    {
    1,0}, {1,1}, {1,2}, {2,0}, {2,1}, {2,2}};
    int h(char board[][SIZE]){
    int cost = 0;
    for(int i=0; i<SIZE; ++i)
    for(int j=0; j<SIZE; ++j){
    if(board[i][j] != SIZE*SIZE){
    cost
    += abs(i - goal_state[board[i][j]-1][0]) +
    abs(j
    - goal_state[board[i][j]-1][1]);
    }
    }
    return cost;
    }

    int step[4][2] = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};//u, l, r, d
    char op[4] = {'u', 'l', 'r', 'd'};

    char solution[1000];
    int bound; //上界
    bool ans; //是否找到答案
    int DFS(int x, int y, int dv, char pre_move){// 返回next_bound
    int hv = h(board);
    if(hv + dv > bound)
    return dv + hv;
    if(hv == 0){
    ans
    = true;
    return dv;
    }

    int next_bound = 1e9;
    for(int i=0; i<4; ++i){
    if(i + pre_move == 3)//与上一步相反的移动
    continue;
    int nx = x + step[i][0];
    int ny = y + step[i][1];
    if(0<=nx && nx<SIZE && 0<=ny && ny<SIZE){
    solution[dv]
    = i;
    swap(board[x][y], board[nx][ny]);

    int new_bound = DFS(nx, ny, dv+1, i);
    if(ans)
    return new_bound;
    next_bound
    = min(next_bound, new_bound);

    swap(board[x][y], board[nx][ny]);
    }
    }
    return next_bound;
    }

    void IDA_star(int sx, int sy){
    ans
    = false;
    bound
    = h(board);//初始代价
    while(!ans && bound <= 100)//上限
    bound = DFS(sx, sy, 0, -10);
    }

    int main(){
    freopen(
    "in", "r", stdin);

    int sx, sy;//起始位置
    char c;
    for(int i=0; i<SIZE; ++i)
    for(int j=0; j<SIZE; ++j){
    cin
    >>c;
    if(c == 'x'){
    board[i][j]
    = SIZE * SIZE;
    sx
    = i;
    sy
    = j;
    }
    else
    board[i][j]
    = c - '0';
    }

    IDA_star(sx, sy);

    if(ans){
    for(int i=0; i<bound; ++i)
    cout
    <<op[solution[i]];
    }
    else
    cout
    <<"unsolvable";

    return 0;
    }
  • 相关阅读:
    bootstrap学习笔记一: bootstrap初认识,hello bootstrap(下)
    bootstrap学习笔记一: bootstrap初认识,hello bootstrap(上)
    AutoCompleteTextView的使用
    常用的android弹出对话框
    PopupWindow的使用
    linux udev、mdev 介绍
    linux 守护进程编程
    linux 下的文件目录操作之遍历目录
    linux 下查找图片文件方法
    linux 内核 zImage 生成过程分析
  • 原文地址:https://www.cnblogs.com/liyongmou/p/1780861.html
Copyright © 2011-2022 走看看