码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 1043 八数码问题

时间:2018-03-24 20:36:25      阅读:190      评论:0      收藏:0      [点我收藏+]

标签:pru   bubuko   所有路径   c++   扩展   std   路径   _id   中间   

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

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

 

第一种 用时 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
*/

 

 

第二种方法:   技术分享图片

代码如下

#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
*/

 

非常经典的题目,建议大家做做,有很多优秀博客。

HDU 1043 八数码问题

标签:pru   bubuko   所有路径   c++   扩展   std   路径   _id   中间   

原文地址:https://www.cnblogs.com/yuluoluo/p/8641009.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!