HDU 1043 八数码问题

Posted 三人行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 1043 八数码问题相关的知识,希望对你有一定的参考价值。

附上题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043

我用了3种方法AC。第一种是双向广搜 + 逆序对奇偶剪枝 + 康拓展开 。 第二种方法是打表法,先用bfs搜素出所有路径,保存。当然还有康拓展开。第二种速度快多了。

第三种方法 A*算法 。关于A*算法大家可以参考大神的博客。

 

第一种 用时 1880MS 代码如下:

#include <bits/stdc++.h>
#define MAX 400000
#define N 9
using namespace std;
/*
  双向广搜,解决八数码问题。 
  这题还要 进行奇偶剪枝,只有当初态和终态的逆序对和奇偶性相同才有解。
  终态逆序对和为0 是偶数,所以在搜索时,某个状态是奇那么肯定不行。不必保存该状态了 
*/ 
int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
char f[]={\'r\',\'l\',\'u\',\'d\'};//0,1,2,3  左右上下 
int fc[4][2]={{0,1},{0,-1},{-1,0},{1,0}};

struct no
{
  int id;
  short c;
}dis[MAX];//记录路径 
 
struct node
{
  string str;
  int id;
  node (const string s1,const int t):str(s1),id(t){}; 
  node(){
  }
};

queue<node> q1;
queue<node> q2;
int vis[MAX];

string Sstr = "123456789";
string Tstr = "123456789"; 
string news = "123456789";
int  Tstr_id ,Sstr_id;
int found = 0;
int ANS_t,ANS_o;
int op;

//奇偶剪枝 
bool pruning(string str)
{
   int sum = 0;
   for (int i=0;i<N;++i)
   {  if (str[i] == \'9\')continue;
        for (int j=i+1;j<N;++j)
          if (str[j]!=\'9\' && str[j]<str[i]) 
           sum++;
   } 
   if (sum%2) return false;
   return true;
}


int kt(string a)
{
    int ans = 0;
    for (int i = 0;i<N;++i)
     {
          int t =0;
          for (int j=i+1;j<N;++j)
            if (a[j] < a[i]) t++;
          ans+= t*fac[N-i-1]; 
     }
     return ans;
}

bool change(string & s,int v,int i)
{
   int x = i/3;
   int y = i%3;
   int cx = x+fc[v][0];
   int cy = y+fc[v][1];
   if (cx >=0 && cx < 3 && cy>=0 && cy < 3)
    {
        int j  = cx*3 + cy;
        news = s;
        char t = news[j];
        news[j] = news[i];
        news[i] = t; 
        //形成新状态后,检测该状态的奇偶性 
        if (pruning(news))    return true;
        else return false;
    }
    else return false;
}

int put (int i)
{
      if (i==0) return 1;
      if (i==1) return 0;
      if (i==2) return 3;
      if (i==3) return 2;
}
//双向bfs 的扩展 
void dbfs_expend(node & s,bool Flag,int g,int i)
{   
        if (!change(s.str,i,g)) return;
        int v = kt(news);
        if (Flag) //正向 
      { 
        if (vis[v]!=1)
        {
          if (vis[v] == 2)//在队列二中出现过,也就是双在此交汇了
          {
              ANS_t = v;
              ANS_o = s.id;
              op = i;//衔接操作 
              found = 1;
              return ;
          } 
        vis[v]  = 1;
        dis[v].c = i;dis[v].id = s.id;
          q1.push(node(news,v));
        }
      } 
      else //反向 
      {
        if (vis[v]!=2)
        {
          if (vis[v]==1)  //交汇了 
          {
              ANS_t = s.id;
              ANS_o = v;
            op = put(i);
              found = 1;
              return ;
          }
         vis[v]  = 2;
         dis[v].c = put(i);dis[v].id = s.id;
           q2.push(node(news,v));
        }
          
      }     
} 

void dbfs()
{
  while(!q1.empty()) q1.pop();
  while(!q2.empty()) q2.pop();
  node s1(Sstr,Sstr_id);
  node s2(Tstr,Tstr_id);
  q1.push(s1); //正向搜索队列 
  q2.push(s2);    //反向搜索队列 
  vis[Sstr_id] = 1;
  vis[Tstr_id] = 2; 
  node temp;
  while(!q1.empty() || !q2.empty())
   {
           if (!q1.empty())
           {
             temp = q1.front();q1.pop();
             int g = 0;
          while(temp.str[g]!=\'9\') ++g;
             for (int i=0;i<4;++i)
             {
                 dbfs_expend(temp,true,g,i); 
                 if (found) return;//找到了 
             }
           }
           
           if (!q2.empty())
           {
                temp = q2.front();q2.pop();
             int g = 0;
          while(temp.str[g]!=\'9\') ++g;
             for (int i=0;i<4;++i)
             {
                 dbfs_expend(temp,false,g,i); 
                 if (found) return ;//找到了 
             }
           }
          
   }
}

void init()
{
  memset(vis,0,sizeof(vis));
  found = 0; 
}

void putans()
{
  if (!found)
   printf ("unsolvable\\n");
  else
  {
      char ans[MAX];
      int i = 0;
      int j = ANS_o;
      while(j != Sstr_id)
      {
        ans[i++] = f[dis[j].c];
        j = dis[j].id;
      }
   for (int j=i-1;j >= 0; --j)  //这里注意,ans数组的存储顺序,很明显,要倒着输出 
      {
        printf ("%c",ans[j]);
      }
      //中间有个衔接步骤,别忘了输出
      printf ("%c",f[op]) ;
      //接着从 ANS_t 到回去,把从终点出发扩散的道路输出
        j = ANS_t;
    while(j != Tstr_id)
    {
       printf ("%c",f[dis[j].c]) ;
          j = dis[j].id;
    }
     printf ("\\n");
  }
}

int main ()
{
   char ch;
   Tstr_id = kt(Tstr);
  while(cin >> ch)
  {    
      if (ch == \'x\')
       {
         Sstr[0]=\'9\';
       }
      else Sstr[0] = ch;
      for (int i=1;i<=8;++i)
       {
           cin >> ch;
           if (ch == \'x\')
         {
           Sstr[i] = \'9\';
         }
          else Sstr[i] = ch;    
       }
       if(pruning(Sstr)) 
       {
        Sstr_id = kt(Sstr);
        init();
        dbfs();
        putans();
       }
       else printf("unsolvable\\n");
  }
  
  return 0;    
}
/*
ullddrurdllurdruldr
ullddrurdllurdruldr
*/

 

 

第二种方法:  最初用的string 来存储状态字符,影响了速度,改为char数组后速度提升了

    

速度提升不少。可喜可贺啊。

string代码如下

#include <bits/stdc++.h>
#define MAX 400000
#define N 9
using namespace std;
/*
  bfs + 打表预处理 + 奇偶剪枝,解决八数码问题。 
  这题还要 进行奇偶剪枝,只有当初态和终态的逆序对和奇偶性相同才有解。
  终态逆序对和为0 是偶数,所以在搜索时,某个状态是奇那么肯定不行。不必保存该状态了 
*/ 
int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
char f[]={\'r\',\'l\',\'u\',\'d\'};//0,1,2,3  左右上下 
int fc[4][2]={{0,1},{0,-1},{-1,0},{1,0}};

struct no
{
  int id;
  short c;
  no(){ id = -1;c = -1;
  }
}dis[MAX];//记录路径 
 
struct node
{
  string str;
  int id;
  node (const string s1,const int t):str(s1),id(t){}; 
  node(){
  }
};

queue<node> q1;

string Sstr = "123456789";
string Tstr = "123456789"; 
string news = "123456789";
int  Tstr_id ,Sstr_id;

int kt(string a)
{
    int ans = 0;
    for (int i = 0;i<N;++i)
     {
          int t =0;
          for (int j=i+1;j<N;++j)
            if (a[j] < a[i]) t++;
          ans+= t*fac[N-i-1]; 
     }
     return ans;
}

bool change(string & s,int v,int i)
{
   int x = i/3;
   int y = i%3;
   int cx = x+fc[v][0];
   int cy = y+fc[v][1];
   if (cx >=0 && cx < 3 && cy>=0 && cy < 3)
    {
        int j  = cx*3 + cy; //计算出移动到的位置的下标 
        news = s;
        char t = news[j];
        news[j] = news[i];
        news[i] = t;
        return true;
    }
    return false;
}

int put(int i)
{
    if (i==0) return 1;
    if (i==1) return 0;
    if (i==2) return 3;
    if (i==3) return 2;
}

void bfs()
{
  node s1(Tstr,Tstr_id);
  q1.push(s1); //
  dis[Tstr_id].id = Tstr_id;
  node temp;
  while(!q1.empty())
   {
             temp = q1.front();q1.pop();
             int g = 0;
          while(temp.str[g]!=\'9\') ++g;
             for (int i=0;i<4;++i)
             {
                 if (!change(temp.str,i,g)) continue;
              int v = kt(news);
              if (dis[v].id == -1)
                   {
                 dis[v].c = put(i);
                 dis[v].id = temp.id;
                   q1.push(node(news,v));
             }
             }
   } 
}

void putans()
{
      int j = Sstr_id;
      while(j != Tstr_id)
      {
       printf ("%c",f[dis[j].c]);
        j = dis[j].id;
      }
      printf ("\\n");
}

int main ()
{
   char ch;
   Tstr_id = kt(Tstr);
   bfs();
  while(cin >> ch)
  {    
      if (ch == \'x\')
       {
         Sstr[0]=\'9\';
       }
      else Sstr[0] = ch;
      for (int i=1;i<=8;++i)
       {
           cin >> ch;
           if (ch == \'x\')
         {
           Sstr[i] = \'9\';
         }
          else Sstr[i] = ch;    
       }
       Sstr_id = kt(Sstr);
       if(dis[Sstr_id].id != -1) 
         putans();
       else 
       printf("unsolvable\\n");
  }
  return 0;    
}
/*
ullddrurdllurdruldr
ullddrurdllurdruldr
*/

 

char 数组版 最快版本 

 

  1 #include <bits/stdc++.h>
  2 #define MAX 400000
  3 #define N 9
  4 using namespace std;
  5 /*
  6   bfs + 打表预处理 ,解决八数码问题。 
  7   没有用string 速度快多了 
  8 */ 
  9 int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};//康拓展开所需的阶乘 
 10 int fc[][2]={{1,0},{-1,0},{0,-1},{0,1}};
 11 char f[]="dulr";
 12 
 13 struct no
 14 {
 15   int id; //上一个的id 记录下来 
 16   short c;//操作 
 17   no()    {id = -1;c = -1;}
 18 }dis[MAX];//记录路径是结构体 
 19  
 20 struct node
 21 {
 22   char str[10];//状态 
 23   int id;//状态对应的id 
 24   node(){}
 25 };
 26 
 27 queue<node> q1; 
 28 int  Tstr_id  = 0;
 29 int Sstr_id;
 30 
 31 int kt(node a)//康拓展开的计算函数 
 32 {
 33     int ans = 0;
 34     for (int i = 0;i<N;++i)
 35      {
 36           int t =0;
 37           for (int j=i+1;j<N;++j)
 38             if (a.str[j] < a.str[i]) t++;
 39           ans+= t*fac[N-i-1]; 
 40      }
 41      return ans;
 42 }
 43 
 44 int put(int i) //因为我们从终态逆着到其他各种能到达的状态,所以,只有倒过来操作, 
 45 {                 //在输出答案时就很方便了 
 46     if (i==0) return 1;
 47     if (i==1) return 0;
 48     if (i==2) return 3;
 49     if (i==3) return 2;
 50 }
 51 
 52 void bfs()
 53 {
 54   node s1;
 55   for (int i=0;i<9;++i) s1.str[i] = \'0\'+i+1;s1.id=0;
 56   q1.push(s1); //起点 “123456789” 
 57   dis[Tstr_id].id = Tstr_id;
 58   node temp,v;
 59   while(!q1.empty())
 60    {
 61              temp = q1.front();q1.pop();
 62              int g = 0;
 63           while(temp.str[g]!=\'9\') ++g;//找到\'9\'的位置 
 64              for (int i=0;i<4;++i)
 65              {
 66                   v = temp;
 67                  int cx = g/3+fc[i][0];
 68                int cy = g%3+fc[i][1];
 69               if (cx <0 || cx >= 3 || cy < 0 || cy >= 3) continue;
 70              swap(v.str[g],v.str[cx*3 + cy]);//交换,状态变换完成,形成新的状态字符 
 71                  v.id = kt(v);
 72               if (dis[v.id].id == -1)//该编号不存在父节点 
 73                    {   // 路径的记录 
 74                  dis[v.id].c = put(i);
 75                  dis[v.id].id = temp.id;
 76                    q1.push(v);
 77              }
 78              }
 79    } 
 80 }
 81 
 82 void putans() 
 83 {
 84       int j = Sstr_id;
 85       while(j != Tstr_id)
 86       {
 87        printf ("%c",f[dis[j].c]);
 88         j = dis[j].id;
 89       }
 90       printf ("\\n");
 91 }
 92 
 93 int main ()
 94 {
 95    char ch;
 96    bfs();
 97    node s;
 98   while(cin >> ch)
 99   {    
100       if (ch == \'x\')
101          s.str[0]=\'9\';
102       else s.str[0] = ch;
103       for (int i=1;i<=8;++i)
104        {
105            cin >> ch;
106            if (ch == \'x\')
107                 s.str[i] = \'9\';
108           else s.str[i] = ch;    
109        }
110        Sstr_id = kt(s);
111        if(dis[Sstr_id].id != -1) 
112          putans();
113        else 
114        printf("unsolvable\\n");
115   }
116   return 0;    
117 }

 

 

 

 

 

 

第三种:

A* 算法解决  ,可以参考别人代码,我的乱七八糟的。https://blog.csdn.net/xiaosshhaa/article/details/54315981

Hdu 1043 Eight (八数码问题)

HDU 1043 八数码问题的多种解法

HDU 1043 Eight八数码解题思路(bfs+hash 打表 IDA* 等)

HDU 1043 Eight(八数码)

hdu 1043 Eight (八数码问题)BFS+康拓展开

八数码的A*与IDA*算法-搜索进阶练习1