zoukankan      html  css  js  c++  java
  • 八数码难题 hdu1043/ poj1077

    一组数据 ,正向搜索。 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         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<<path<<endl;
    144         else
    145 
    146             printf("unsolvable
    ");
    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! = 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<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
    ");
        }
        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! = 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<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
    ");
    
        }
        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的深度

  • 相关阅读:
    POJ3613 Cow Relays [矩阵乘法 floyd类似]
    BZOJ 2302: [HAOI2011]Problem c [DP 组合计数]
    BZOJ 1037: [ZJOI2008]生日聚会Party [序列DP]
    BZOJ 1898: [Zjoi2005]Swamp 沼泽鳄鱼 [矩阵乘法]
    Codeforces Round #390 (Div. 2)
    BZOJ 1096: [ZJOI2007]仓库建设 [斜率优化DP]
    BZOJ 1911: [Apio2010]特别行动队 [斜率优化DP]
    BZOJ 1597: [Usaco2008 Mar]土地购买 [斜率优化DP]
    [斜率优化DP]【学习笔记】【更新中】
    BZOJ 2127: happiness [最小割]
  • 原文地址:https://www.cnblogs.com/zn505119020/p/3586061.html
Copyright © 2011-2022 走看看