HDU 1430 魔板

在魔方风靡全球之后不久,Rubik先生发明了它的简化版——魔板。魔板由8个同样大小的方块组成,每个方块颜色均不相同,可用数字1-8分别表示。任一时刻魔板的状态可用方块的颜色序列表示:从魔板的左上角开始,按顺时针方向依次写下各方块的颜色代号,所得到的数字序列即可表示此时魔板的状态。例如,序列(1,2,3,4,5,6,7,8)表示魔板状态为:


1 2 3 4
8 7 6 5

对于魔板,可施加三种不同的操作,具体操作方法如下:

A: 上下两行互换,如上图可变换为状态87654321
B: 每行同时循环右移一格,如上图可变换为41236785
C: 中间4个方块顺时针旋转一格,如上图可变换为17245368

给你魔板的初始状态与目标状态,请给出由初态到目态变换数最少的变换步骤,若有多种变换方案则取字典序最小的那种。

hdu 链接:http://acm.hdu.edu.cn/showproblem.php?pid=1430

 

BFS + 康拓展开 + 打表 + 映射 

 

#include <bits/stdc++.h> 
const int N = 8; 
const int MAX = 40323;
using namespace std;

/*
魔板 hdu 1430 搜索 + 康拓展开。
 http://acm.hdu.edu.cn/showproblem.php?pid=1430
 
BFS + 打表预处理 + 康拓展开 
https://www.cnblogs.com/H-Vking/p/4346004.html

关于打表预处理:

  由于魔板的所有状态都可以转换为“12345678”,
所以这时就需要做一个映射:每组数据都有一个起始状态与目标状态,
可以把起始状态用一种映射关系映射为“12345678”,
然后用这种映射关系再去改一下终止状态。例如:初态为“12653487” , 
目态为“12345678” ;这时映射后初态为“12345678”,
即f[1] = 1 , f[2] = 2 , f[6] = 3 , f[5] = 4 , f[3] = 5 , 
f[4] = 6 , f[8] = 7 , f[7] = 8 ,按照这种
映射关系目态应为“12564387”。
代码应为:f[start[i] - '0'] = i ; end[i] = f[end[i] - '0'] + '0';

 有这样一个映射前提,
可以先用BFS预处理从“12345678”到其余所有状态的步骤,
然后输入每组测试数据后进行转换,然后这时候就变成了
求从“12345678”到映射后的目标状态的步骤的问题,
这时按照存好的路径输出即可。
*/

struct no
{
  int num;//上一状态的编号 
  int c;
  no(){
      num = -1;c =-1;
  }
}dis[MAX]; 

typedef struct 
{
    int num;//对应kt编号 
    string st;
}node;

int fac[] = {1,1,2,6,24,120,720,5040,40320};
string str;//初始状态 
string Tstr;//目标状态 
int Tflag ;
int q;

queue<node> qu;

//康拓的逆(这题不需要)
void ktn(char a[9],int k)
{  
    k--;//这别忘了,第12个,一定从11开始计算 
   int vis[10]={0};int j = 0;
   for (int i=0;i<N;++i)
    {
        int t = k/fac[N-i-1];
        for ( j=1;j<=N;++j) //计算出它到底是几,这里要排除出现过的数 
          if (!vis[j])
            {
              if (!t) break;
              t--;
            }
        a[i]=j+'0';
        vis[j] =1; 
        k %= fac[N-i-1]; 
    }
    
} 

//康拓展开
 
int kt(string &arr)
{
    int ans =  0 ;
   for (int i=0;i<N;++i)
    {
        int t = 0; //记录后面比它小的数字个数 
      for (int j = i+1;j<N;++j)
       {
            if (arr[j] < arr[i]) t++;
       }
      ans += t*fac[N-i-1]; 
    }
    return ans;
}

string change (string t,int s)
{   
     string a(t);
    if (s == 1)
     {
         for(int i=N-1;i>=0;--i)
           a[N-i-1] = t[i];
     }
    else if (s == 2)
    {    
          a[0] = t[3];
          a[7] = t[4];
          int v = 1;
       for (int i=0;i<N;++i)
         {
             if (i==3 || i == 4) continue;
             a[v++] = t[i];
         }
    }
    else
     {
       char s1 = t[1];char s2 = t[2];
       char s5 = t[5];char s6 = t[6];
       a[2] = s1; a[5] = s2;a[6] = s5; a[1]=s6;
     }
    return a;
}

void bfs()
{  
   node temp ;
   string s;
   int v;
  while(!qu.empty())
   {
        temp = qu.front();qu.pop();
        for (int i=1;i<=3;++i) // A,B,C
         {
              s = change(temp.st,i);
              v =kt(s);
             if(dis[v].num == -1)
          {
              node t ;t.st = s;t.num = v;
              qu.push(t);
              dis[v].num = temp.num;dis[v].c =i;
          } 
         }
   }
}
void putans()
{
   char ans[MAX];
   int i = 0;
   int j = Tflag;
   while(j!= q)
   {
       ans[i++] = dis[j].c+'A'-1;
    j = dis[j].num;
   }    
   for (int j = i-1;j>=0;--j)
     printf ("%c",ans[j]);
     printf ("\n");
} 

 int main ()
 {    
       str = "12345678";
       char s1[10];
       q = kt(str);
       node t ;t.st = str;t.num = q;
       qu.push(t);
       bfs();  //打表预处理,从12345678 到其他态的路径算出 
   while(cin>>str>>Tstr)
    { 
//利用一种映射关系 将 起始映射为12345678 对应的终态也就映射成另一个了
//然后就可以查询了(比如 23456781 -- 12345678的映射,就是,2对应1,3对应2...)
        //映射
        for (int i=0;i<N;++i)
           s1[str[i]-'0'] = i+1; //这里,s1 数组就相当于一个函数了,来映射 
        for (int i=0;i<N;++i)
           Tstr[i] = s1[Tstr[i]-'0'] +'0';
        Tflag = kt(Tstr);
        putans();
    } 
    
  return 0;
}/*
63728145
86372541
*/ 

 

posted @ 2018-03-23 22:28  雨落洛  阅读(209)  评论(0编辑  收藏  举报