A* \IDA* 分析总结

来源:互联网 发布:xp 数据执行保护 编辑:程序博客网 时间:2024/06/06 17:10

   

经典八皇后问题:

 

#include <iostream>#include <algorithm>#include <queue>#include <vector>#include <cstdio>#include <cstring>using namespace std;struct node{    int tab[3][3];    int r,c;    int hash_val;    //当前排列在全排列时候的大小位置    node* pre;    int op;    int f,g;         //f:估计值 g:从起始点到当前点的花费    bool operator < (const node& rhs)const{         return f > rhs.f;    }}st,ed;node t[370000];int tot;int hash_tab[370000];priority_queue<node> open;int fn[10];int ed_map[10][2];int dir_i[4] = {0,0,1,-1};int dir_j[4] = {1,-1,0,0};char print_op[4] = {'r','l','d','u'};//处理输入的字符void In(char s[],node& t){    char ch;    int next = 0;    for(int i = 0;i < 3;++i){        for(int j = 0;j < 3;++j){            while(s[next] == ' ') ++next;            ch = s[next++];            if(isdigit(ch))                st.tab[i][j] = ch - '0';            else {          // x 的位置                st.r = i;                st.c = j;                st.tab[i][j] = 0;            }        }    }}//获取估价函数值int get_f(node a,int g){    int h = 0;    for(int i = 0;i < 3;++i)        for(int j = 0;j < 3;++j)           if(a.tab[i][j])     //距离目标点的曼哈顿距离              h += abs(i - ed_map[a.tab[i][j]][0]) + abs(j - ed_map[a.tab[i][j]][1]);    return g + h;}//康拓展开,判断该组合在所有排列中的位置int get_hash(node a){    int ret;    ret = 0;    int num = 8;    for(int i = 0;i < 3;++i){        for(int j = 0;j < 3;++j){            int x = 0;            //当前这个数后有多少个比他小            for(int jj = j + 1;jj < 3;++jj)                if(a.tab[i][jj] < a.tab[i][j]) x++;            for(int ii = i + 1;ii < 3;++ii)                for(int jj = 0;jj < 3;++jj)                   if(a.tab[ii][jj] < a.tab[i][j]) x++;            ret += fn[num] * x;             //康拓展开            num--;        }    }    return ret;}//预处理void init(){     memset(hash_tab,0,sizeof(hash_tab));     while(!open.empty()) open.pop();     tot = 0;     st.f = get_f(st,0);       //获取估价函数值     st.g = 0;     st.hash_val = get_hash(st);  //获取该组合在全排列的位置     open.push(st);     hash_tab[st.hash_val] = 1;    //当前的组合排列已经遍历过了}void pre(){    //预处理阶乘   fn[0] = 1;   for(int i = 1;i < 9;++i) fn[i] = i * fn[i - 1];   //预处理结果   for(int i = 0;i < 3;++i)      for(int j = 0;j < 3;++j)         ed.tab[i][j] = (i * 3) + j + 1;   ed.tab[2][2] = 0;   ed.hash_val = get_hash(ed);   //预处理结果位置的映射   for(int i = 0;i < 3;++i)     for(int j = 0;j < 3;++j)        if(ed.tab[i][j]){            ed_map[ed.tab[i][j]][0] = i;            ed_map[ed.tab[i][j]][1] = j;        }}//判断逆序数的奇偶性int get_preval(node a){   int ret = 0;   for(int i = 0;i < 3;++i)     for(int j = 0;j < 3;++j){        if(!a.tab[i][j]) continue;        int x = 0;        for(int jj = j + 1;jj < 3;++jj)            if(a.tab[i][jj] && a.tab[i][jj] < a.tab[i][j]) x++;        for(int ii = i + 1;ii < 3;++ii)            for(int jj = 0;jj < 3;++jj)               if(a.tab[ii][jj] && a.tab[ii][jj] < a.tab[i][j]) x++;        ret += x;     }     return ret & 1;}bool pre_solve(){    return(get_preval(st)^(get_preval(ed)));}void change(node &tmp,node a,int nextr,int nextc,int _i,int idx){    for(int i = 0;i < 3;++i)        for(int j = 0;j < 3;++j)           tmp.tab[i][j] = a.tab[i][j];    swap(tmp.tab[nextr][nextc],tmp.tab[a.r][a.c]);    tmp.hash_val = get_hash(tmp);    tmp.r = nextr;    tmp.c = nextc;    tmp.pre = &t[idx];    tmp.op = _i;              //移动的方向    tmp.g = a.g + 1;          //当前的步数    tmp.f = get_f(tmp,tmp.g);}bool check(int i,int j){    if(i > 2 || i < 0 || j > 2 || j < 0) return false;    return true;}//打印路径void path(node* a){    if(a->hash_val == st.hash_val) return ;    path(a->pre);    printf("%c",print_op[a->op]);}//A*void Astar(){    int nextr,nextc;    node ans = st;    int fla = 0;    if(st.hash_val != ed.hash_val)    while(!open.empty()){        node a = open.top();        open.pop();        t[++tot] = a;        for(int i = 0;i < 4;++i){            nextr = a.r + dir_i[i];            nextc = a.c + dir_j[i];            if(check(nextr,nextc)){                node tmp;                change(tmp,a,nextr,nextc,i,tot);                if(hash_tab[tmp.hash_val]) continue;                if(tmp.hash_val == ed.hash_val){                    fla = 1;                    ans = tmp;                    break;                }                open.push(tmp);            }        }        if(fla) break;        hash_tab[a.hash_val] = 1;    }    path(&ans);    puts("");}int main(){   // freopen("Input.txt","r",stdin);    char ss[12];    while(gets(ss)){        pre();        In(ss,st);        init();        if(pre_solve())            puts("unsolvable");        else            Astar();    }    return 0;}


 

 

 

对于空格(0)的左移/右移操作,对应序列不变(逆序数也就不变) 
对于空格(0)的上移/下移操作,相当于序列的某个数字前移/后移两位,该序列的逆序数奇偶性不变。 
所以求初始状态与目标状态的逆序数可作出判断 
例中前者为奇,后者为偶,因此无解

利用奇偶性判断所给出的初始状态有无解. 

判别方法是: 
以数组为一维的举例子. 
将八数码的一个结点表示成一个数组a[9],空格用0表示,设临时函数p(x)定义为:x数所在位置前面的数比x小的数的个数, 
其中0空格不算在之内,那设目标状态为b[9],那r=sigma(p(x)) sigma()表示取所有的x:1-8并求和, 
那对于初始状态a[9],t=sigma(p(x)),如果r和t同为奇数或者同为偶数,那么该状态有解,否则无解。 

考虑到四种移动方法对sigma(p(x))的影响,左移和右移是不会影响它的值的, 
更不会影响奇偶性,如果是上移或者下移就会影响: 
上移:一次上移会使一个元素向前跳两个数字的位置,设这两个数字为a1,a2, 
不妨设a1<a2,移的这个数字设为a0,那无非只有以下三次情况: 
1,a0<a1<a2,考虑它们三者的p(x)值,p(a0)不变,p(a1)++,p(a2)++,总体增加了2 
2,a1<a0<a2,p(a0)--,p(a1)不变,p(a2)++,总体不变 
3,a1<a2<a0,p(a0)-=2,p(a1),p(a2)不变,总体减小了2 

综合起来的结论就是不会影响sigma(p(x))的奇偶性。

 

 

1 0
原创粉丝点击