zoj1217

题目大意:

给一个3*3的矩阵,通过‘x’(可以看成空格)移动,使得最后的矩阵形式为标准型。

解题思路:

自己一脸懵逼,从网上找了其他人的思路,大概看懂了。
9!=362880,所以定义一个F数组,记录当前状态属于第几种状态。
dxdy数组第i为可以移动到几号位,qs记录开始序列,qe记录结束序列。
这里我们介绍一种可以将全排列的一种表示为“第几个排列”的方法——康托展开。

首先看几个康托展开的实例(9的全排列):

1 2 3 4 5 6 7 8 9——展开为 0。

1 2 3 4 5 6 7 9 8——展开为 1。

1 2 3 4 5 6 8 7 9——展开为 2。

由这些最开始的方法我们可以发现一个规律:从第一个数开始,依次判断判断这些数是当前没有出现过的数的第几个(由0开始),记为a1, a2, … ,a(n - 1)。不难发现如1 2 3 4 5 6 8 7 9,由1至6都是当前没有出现过的第0个数,而8是7,8,9中的第1个(由0开始),9是7,9中的第1个,7是第0个。故a1 = a2 = … = a6 = 0,a7 = 1,a8 = 1,a9 = 0。
之后排列数(康托展开的值)等于
a1 * (n - 1)! + a2 * (n - 2)! + … + ak * (n - k)! + … + an * 0!
再举几个例子:
3 5 7 4 1 2 9 6 8——展开为 98884。
5 6 7 8 1 2 3 4 9——展开为 184800。
至于双向BFS 就是知道初始和目标状态的情况下,就可以正向反向一起广搜,找到一个另一方向已搜到过的节点就得到解了
一般来讲每次选择队列中节点比较少的一边来进行下一次搜索。
双向BFS的代码

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const string des="123456780";
int F[362881];
int fact[9]={1,1,2,6,24,120
,720,5040,40320};
string beg="123456780";
int xpos;
int dxdy[10][4]=
{
  {-1,-1,1,3},
  {-1,0,2,4},
  {-1,1,-1,5},
  {0,-1,4,6},
  {1,3,5,7},
  {2,4,-1,8},
  {3,-1,7,-1},
  {4,6,8,-1},
  {5,7,-1,-1}
};
char dir[4]={'u','l','r','d'};
struct node
{
  string cur;
  string op;
  int pos;
}qs[362881],qe[362881];

long hashfun(const string&s)
{
  long r=0;
  for(int i=1;i<9;i++)
  {
    int num=0;
    if(i<5)
    {
      for(int j=0;j<i;j++)
      {
        if(s[j]>s[i])
          ++num;
      }
    }
    else
    {
      for(int j=i+1;j<9;j++)
      {
        if(s[j]>s[i])
          ++num;
      }
      num=8-(s[i]-'0')-num;
    }
    r+=num*fact[i];
  }
  return r;
};

string BFS()
{
  int head1,head2,rear1,rear2;
  head1=head2=0;
  rear1=rear2=1;
  qs[0].cur=beg;
  qs[0].op = "";
  qs[0].pos=xpos;
  F[hashfun(beg)]=1;
  qe[0].cur=des;
  qe[0].op="";
  qe[0].pos=8;
  F[hashfun(des)]=2;
  while(rear1+rear2<181441)
  {
    if(rear1-head1<rear2-head2)
    {
      if(head1<rear1)
      {
        node temp=qs[head1];
        ++head1;
        int pos=temp.pos;
        for(int i=0;i<4;i++)
        {
          if(dxdy[pos][i]<0)
            continue;
          int cpos=dxdy[pos][i];
          string ts=temp.cur;
          ts[pos]=ts[cpos];
          ts[cpos]='0';
          long hv=hashfun(ts);
          string cop=temp.op+dir[i];
          if(F[hv]%10==2)
            return cop+qe[F[hv]/10].op;
          else if(F[hv]==0)
          {
            F[hv]=rear1*10+1;
            qs[rear1].cur=ts;
            qs[rear1].op=cop;
            qs[rear1++].pos=cpos;
          }
        }
      }
    }
    else 
    {
      if(head2<rear2)
      {
        node temp=qe[head2];
        ++head2;
        int pos=temp.pos;
        for(int i=0;i<4;i++)
        {
          if(dxdy[pos][i]<0)
            continue;
          int cpos=dxdy[pos][i];
          string ts=temp.cur;
          ts[pos]=ts[cpos];
          ts[cpos]='0';
          long hv=hashfun(ts);
          string cop=dir[3-i]+temp.op;
          if(F[hv]%10==1)
          {
            return qs[F[hv]/10].op+cop;
          }
          else if(F[hv]==0)
          {
            F[hv]=rear2*10+2;
            qe[rear2].cur=ts;
            qe[rear2].op=cop;
            qe[rear2++].pos=cpos;
          }
        }
      }
    }
  }  
};

int count(const string&s)
{
  int ct=0;
  for(int i=1;i<9;i++)
  {
    if(s[i]=='0')
      continue;
    for(int j=0;j<i;j++)
    {
      if(s[j]>s[i])
        ct++;
    }
  }
  return ct;
};

int main()
{
  while(cin>>beg[0])
  {
    cin>>beg[1]>>beg[2]>>beg[3]>>beg[4]
    >>beg[5]>>beg[6]>>beg[7]>>beg[8];
    for(xpos=0;xpos<9;++xpos)
    {
      if(beg[xpos]=='x')
      {
        beg[xpos]='0';
        break;
      }
    }
    memset(F,0,sizeof(F));
    if(count(beg)&1)
      printf("unsolvable\n");
    else
    {
      string s=BFS();
      int len=s.length();
      for(int i=0;i<len;i++)
        printf("%c",s[i]);
      printf("\n");
    }
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值