八数码难题 hdu1043/ poj1077

来源:互联网 发布:网络爬虫基本原理 编辑:程序博客网 时间:2024/04/29 12:38

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

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

  1 #include<iostream>  2 #include<stdlib.h>  3 #include<stdio.h>  4 #include<math.h>  5 #include<string.h>  6 #include<string>  7 #include<queue>  8 #include<algorithm>  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<i;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_queue<node>q; 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         else128             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             else138                 start.ma[i]=ch-'0';139         }140         start.ans1=hash(start.ma);141         memset(visited,0,sizeof(visited));142         if(bfs())143             cout<<path<<endl;144         else145 146             printf("unsolvable\n");147 148     }149     return 0;150 }

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

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

hash+ 打表+广搜

代码如下:

#include<iostream>#include<stdlib.h>#include<stdio.h>#include<math.h>#include<string.h>#include<string>#include<queue>#include<algorithm>#define N 363000  // 9! = 362880using 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<i;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="";    queue<node>q;    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<<path[start.ans1]<<endl;        else            printf("unsolvable\n");    }    return 0;}

 三:

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

代码如下:

#include<iostream>#include<stdlib.h>#include<stdio.h>#include<math.h>#include<string.h>#include<string>#include<queue>#include<algorithm>#define N 363000  // 9! = 362880using 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<i;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_queue<node>q;    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<i;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<<path<<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的深度

0 0
原创粉丝点击