八数码难题 hdu1043/ poj1077

一组数据 ,正向搜索。 poj 1077

代码如下: A* + hash +堆 + 曼哈顿距离  做一组数据的poj1077,可是,但是对于hdu 1043多组数据,没有剪枝,故超时,下面再给剪枝改进的算法。

  1 #include
  2 #include
  3 #include
  4 #include
  5 #include<string.h>
  6 #include<string>
  7 #include
  8 #include
  9 #define N 363000  // 9! = 362880
 10 using namespace std;
 11 
 12 struct node{
 13     int ma[9];
 14     int ans1; //哈希值
 15     int x;  // 记录x可移动的位置,记为9
 16     int f; // 估价函数
 17     int g;  // 深度
 18     string path;
 19     bool operator <(const node & a)const{
 20         return f>a.f; //优先访问估价函数值f较小的节点
 21     }
 22 };
 23 int visited[N];
 24 string path;
 25 node start;
 26 int end = 0; // 123456789对应的哈希值
 27 int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; // 上-右-下-左
 28 char index[5]="urdl";
 29 // 哈希表,排列逆序,用于判重
 30 int fact[] = {1, 1, 2, 6, 24,120 ,720, 5040, 40320};   // 康托展开数列
 31             //0! 1! 2! 3! 4!  5!   6!   7!     8!
 32 int hash(int *s)  // 返回每个排列的哈希值
 33 {
 34     int sum=0;
 35     for(int i=0;i< 9;i++)
 36     {
 37         int num=0;    // 计算 第i位数的逆序数
 38         for(int j=0;j)
 39             if(s[i]<s[j])
 40                 num++;
 41         sum+= num * fact[i]; // sum的取值范围是 0---9!-1
 42     }
 43     return  sum;
 44 }
 45 int ABS(int x){return x>0?x:(-x);} //求绝对值
 46 int h_juli(int *s) // 不算x时的 曼哈顿距离启发函数,计算的是 从第n个点到目标点的最小代价
 47 {
 48     int tx,ty,endx,endy,tmp;  // 逆序搜索,从目标状态为起点
 49     int sum=0;
 50     for(int i=0;i<9;i++)
 51     {
 52         if(s[i]== 9) continue;
 53         tmp=s[i];
 54         tx= i /3;
 55         ty= i %3 ;
 56         endx = (tmp-1)/3;
 57         endy = (tmp-1)%3;
 58         sum+=ABS(tx-endx) + ABS(ty- endy);
 59     }
 60     return sum;
 61 }
 62 
 63 //广度优先搜索
 64 int bfs()
 65 {
 66     priority_queueq;
 67     node now,next;
 68     int x ,y,ans;
 69     now= start;
 70     now.ans1=hash(now.ma);
 71     now.path="";
 72     if(now.ans1 == end){
 73         path=now.path;
 74         return 1;
 75     }
 76     visited[now.ans1] =1;  // 访问起始节点
 77     now.g = 0; // 深度置为0
 78     now.f = h_juli(now.ma);
 79     q.push(now);  //将顶点now压入队列
 80     while(!q.empty())
 81     {
 82         now =q.top();  //取出队列头的顶点,设为now
 83         q.pop();
 84         x= now.x /3;
 85         y= now.x %3;
 86         for(int i=0;i<4;i++)
 87         {
 88             int tx= x+dir[i][0];
 89             int ty= y+dir[i][1];
 90             if(ty<0 || ty>2 || tx<0 || tx>2) continue;
 91             next=now;   // next 为 now的邻接顶点
 92             next.x= tx*3+ty;
 93             next.ma[now.x] = now.ma[next.x];
 94             next.ma[next.x] = 9;
 95             ans = hash(next.ma);
 96             if(!visited[ans] )  //且next没有访问过,访问next顶点,对next赋值,并将next加入队列
 97             {
 98                 visited[ans]=1;
 99                 next.ans1=ans;
100                 next.g++;
101                 next.f= next.g+h_juli(next.ma);
102                 next.path+=index[i];
103                 if(next.ans1 == end){
104                     path=next.path;
105                     return 1;
106                 }
107                 q.push(next);
108             }
109 
110         }
111 
112     }
113     return 0;
114 }
115 
116 int main()
117 {
118     int i,j;
119     char ch;
120     while(cin>>ch)
121     {
122         if(ch=='x')
123         {
124             start.ma[0]=9;
125             start.x=0;
126         }
127         else
128             start.ma[0]=ch-'0';
129         for( i=1;i<9;i++)
130         {
131             cin>>ch;
132             if(ch=='x')
133             {
134                 start.ma[i]=9;
135                 start.x=i;
136             }
137             else
138                 start.ma[i]=ch-'0';
139         }
140         start.ans1=hash(start.ma);
141         memset(visited,0,sizeof(visited));
142         if(bfs())
143             cout<endl;
144         else
145 
146             printf("unsolvable\n");
147 
148     }
149     return 0;
150 }

但是 hdu 1043 多项数据输入,如果采用正向搜索,绝对超时。

反向搜索,把所有情况打表出来。

hash+ 打表+广搜

代码如下:

#include
#include
#include
#include
#include<string.h>
#include<string>
#include
#include
#define N 363000  // 9! = 362880
using namespace std;
struct node{
    int ma[9];
    int ans1; //哈希值
    int x;  // 记录x可移动的位置
    string path;
};
int visited[N];
string path[N];
node start;
int end = 0; // 123456789对应的哈希值
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; // 上-右-下-左
char index[5]="dlur";    // 下-左-上-右 , 反向搜索,用于打表
int fact[] = {1, 1, 2, 6, 24,120 ,720, 5040, 40320};
int hash(int *s)  // 返回每个排列的哈希值
{
    int sum=0;
    for(int i=0;i< 9;i++)
    {
        int num=0;    // 计算 第i位数的逆序数
        for(int j=0;j)
            if(s[i]<s[j])
                num++;
        sum+= num * fact[i]; // sum的取值范围是 0---9!-1
    }
    return  sum;
}

//广度优先搜索 多项输入,打表,否则 超时
void bfs()
{
    memset(visited,0,sizeof(visited));

    node now,next;
    int x ,y,ans;
    for(int i=0;i<9;i++) now.ma[i]=i+1;
    now.x=8;
    now.ans1=end;
    now.path="";
    queueq;
    q.push(now);
    path[end]="";
    while(!q.empty())
    {
        now =q.front();  //取出队列头的顶点,设为now
        q.pop();
        x= now.x /3;
        y= now.x %3;
        for(int i=0;i<4;i++)
        {
            int tx= x+dir[i][0];
            int ty= y+dir[i][1];
            if(ty<0 || ty>2 || tx<0 || tx>2)
                continue;
            next=now;   // next 为 now的邻接顶点
            next.x= tx*3+ty;
            next.ma[now.x] = now.ma[next.x];
            next.ma[next.x] = 9;
            next.ans1 = hash(next.ma);
            if(!visited[next.ans1] )
            {
                visited[next.ans1]=1;
                next.path=index[i]+next.path;   //字符串反向存储,然后输出
                q.push(next);
                path[next.ans1]=next.path;    // 打表
            }
        }
    }
}
int main()
{
    int i,j;
    char ch;
    bfs();
    while(cin>>ch)
    {
        if(ch=='x')
        {
            start.ma[0]=9;
            start.x=0;
        }
        else
            start.ma[0]=ch-'0';
        for( i=1;i<9;i++)
        {
            cin>>ch;
            if(ch=='x')
            {
                start.ma[i]=9;
                start.x=i;
            }
            else
                start.ma[i]=ch-'0';
        }
        start.ans1=hash(start.ma);
        if(visited[start.ans1])  // 在表里的点
            cout<endl;
        else
            printf("unsolvable\n");
    }
    return 0;
}

 三:

hdu 1043 A* +哈密顿距离+ hash+剪枝 + 堆

代码如下:

#include
#include
#include
#include
#include<string.h>
#include<string>
#include
#include
#define N 363000  // 9! = 362880
using namespace std;

struct node{
    int ma[9];
    int ans1; //哈希值
    int x;  // 记录x可移动的位置,记为9
    int f; // 估价函数
    int g;  // 深度
    string path;
    bool operator <(const node & a)const{
        return f>a.f; //优先访问估价函数值f较小的节点
    }
};
int visited[N];
string path;
node start;
int end = 0; // 123456789对应的哈希值
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}}; // 上-右-下-左
char index[5]="urdl";
// 哈希表,排列逆序,用于判重
int fact[] = {1, 1, 2, 6, 24,120 ,720, 5040, 40320};   // 康托展开数列
            //0! 1! 2! 3! 4!  5!   6!   7!     8!
int hash(int *s)  // 返回每个排列的哈希值
{
    int sum=0;
    for(int i=0;i< 9;i++)
    {
        int num=0;    // 计算 第i位数的逆序数
        for(int j=0;j)
            if(s[i]<s[j])
                num++;
        sum+= num * fact[i]; // sum的取值范围是 0---9!-1
    }
    return  sum;
}
int ABS(int x){return x>0?x:(-x);} //求绝对值
int h_juli(int *s) // 不算x时的 曼哈顿距离启发函数,计算的是 从第n个点到目标点的最小代价
{
    int tx,ty,endx,endy,tmp;  // 逆序搜索,从目标状态为起点
    int sum=0;
    for(int i=0;i<9;i++)
    {
        if(s[i]== 9) continue;
        tmp=s[i];
        tx= i /3;
        ty= i %3 ;
        endx = (tmp-1)/3;
        endy = (tmp-1)%3;
        sum+=ABS(tx-endx) + ABS(ty- endy);
    }
    return sum;
}

//广度优先搜索
int bfs()
{
    priority_queueq;
    node now,next;
    int x ,y,ans;
    now= start;
    now.ans1=hash(now.ma);
    now.path="";
    if(now.ans1 == end){
        path=now.path;
        return 1;
    }
    visited[now.ans1] =1;  // 访问起始节点
    now.g = 0; // 深度置为0
    now.f = h_juli(now.ma);
    q.push(now);  //将顶点now压入队列
    while(!q.empty())
    {
        now =q.top();  //取出队列头的顶点,设为now
        q.pop();
        x= now.x /3;
        y= now.x %3;
        for(int i=0;i<4;i++)
        {
            int tx= x+dir[i][0];
            int ty= y+dir[i][1];
            if(ty<0 || ty>2 || tx<0 || tx>2) continue;
            next=now;   // next 为 now的邻接顶点
            next.x= tx*3+ty;
            next.ma[now.x] = now.ma[next.x];
            next.ma[next.x] = 9;
            ans = hash(next.ma);
            if(!visited[ans] )  //且next没有访问过,访问next顶点,对next赋值,并将next加入队列
            {
                visited[ans]=1;
                next.ans1=ans;
                next.g++;
                next.f= next.g+h_juli(next.ma);
                next.path+=index[i];
                if(next.ans1 == end){
                    path=next.path;
                    return 1;
                }
                q.push(next);
            }

        }

    }
    return 0;
}
int check(int *s) // 剪枝
{
    int i, j,num=0;
    for( i=0;i<9;i++)
    {
        if(s[i] == 9) continue;
        for(j=0;j)
        {
            if(s[j] ==9) continue;
            if(s[j] >s[i])
                num++;
        }
    }
    return num&1;  // 逆序数为偶数,返回0,否则返回1
}
int main()
{
    int i,j;
    char ch;
    while(cin>>ch)
    {
        if(ch=='x')
        {
            start.ma[0]=9;
            start.x=0;
        }
        else
            start.ma[0]=ch-'0';
        for( i=1;i<9;i++)
        {
            cin>>ch;
            if(ch=='x')
            {
                start.ma[i]=9;
                start.x=i;
            }
            else
                start.ma[i]=ch-'0';
        }
        if(check(start.ma)) {puts("unsolvable"); continue;}  // 逆序数 为偶数,可达, 逆序数为奇数,不可达
        start.ans1=hash(start.ma);
        memset(visited,0,sizeof(visited));
        if(bfs())
            cout<endl;
        else

            printf("unsolvable\n");

    }
    return 0;
}

 

 

 

排列哈希函数判重,参见: 

http://blog.csdn.net/tiaotiaoyly/article/details/1720453

 

哈密顿距离 为 两个点 (x1,y1) (x2,y2)  距离= | x1 - x2|  +| y1-  y2|

A*算法

f(n)= g(n) + h(n)  f(n)为估价函数   h(n) 为启发函数,是哈密顿距离 , g(n) 为节点n的深度

你可能感兴趣的:(八数码难题 hdu1043/ poj1077)