题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043
经典的八数码,一般做法直接bfs,肯定tle。应该用A*剪枝或者打表,我用的是打表,打表更快点。
我的思路是以最终状态为起点进行bfs,同时开个结构体数组来记忆bfs过程中的状态(方向,这个八数码状态的上个状态的结构体数组下标),最终可以回溯打印路径。 同时给定一种状态要能找到他对应的结构体数组的下标,所以需要再开个数组存每种状态的记忆数组的下标。 同时在回溯的时候方向是反着的,因为我是以末状态搜起始状态的,本来是左,倒过来就是右了,所以在存方向的时候,需要变换一下。
八数码的状态用康拓值来映射
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define scanfprint() freopen("input.txt","r",stdin)
#define printfprint() freopen("output.txt","w",stdout)
#define mem(a,b) memset(a,b,sizeof(a))
const int spot=1000+10;
const int edge=100000+10;
const int maxn=362880+10;
const double pi=acos(-1.0);
const int mod=1e9+7;
const double ips=0.000001;
const int c_n=9;
bool c_flag[c_n+10];
int c_a[c_n+10],fact[15]= {0,1,1,2,6,24,120,720,5040,40320,362880},m[maxn],num=0; //m为给定康拓值映射的存储路径的结构体下标
char str[10];
struct stu1//打表存储bfs过程的结构体
{
int pre; //上一个状态的下标
char dir; //方向
} memory[maxn];
struct stu2
{
int c_v,num,coor; //分别对应 康拓值,打表的下标,八数码里x的下标
} s,t;
bool flag[maxn]; //
int cantor() //康拓展开 c_a[1]到c_a[n]表示一个全排列,返回的ans表示康拓值(从0开始)
{
mem(c_flag,0);
int i,j,sum=0,ans=0,k;
for(i=1,k=c_n; i<=c_n; i++,k--)
{
sum=0;
for(j=c_a[i]-1; j>=1; j--)
{
if(!c_flag[j])
sum++;
}
c_flag[c_a[i]]=1,ans+=sum*fact[k];
}
return ans;
}
void inverse_cantor(int m) //康拓逆展开 传入的m是康拓值(从0开始),最终的c_a[1]到c_a[n]是m对应的排列
{
mem(c_flag,0);
int i,j,k,sum;
for(i=1,k=c_n; i<=c_n; i++,k--)
{
sum=m/fact[k]+1;
for(j=1; j<=c_n; j++)
{
if(!c_flag[j])
sum--;
if(!sum)
break;
}
c_a[i]=j,c_flag[j]=1,m%=fact[k];
}
}
void bfs()
{
int i,temp;
s.c_v=0,s.num=0,s.coor=9;
flag[s.c_v]=1;
queue q;
q.push(s);
while(!q.empty())
{
s=q.front();
q.pop();
inverse_cantor(s.c_v); //逆康拓,还原八数码的状态
for(i=0; i<4; i++) //四个方向 下 左 右上
{
if(!i) //下
{
if(s.coor<7)
{
swap(c_a[s.coor],c_a[s.coor+3]); //交换位置
t.c_v=cantor(); //得到状态所对应的康拓值
if(!flag[t.c_v])
{
t.coor=s.coor+3,t.num=++num; //9位置变换,
memory[num].dir='u',memory[num].pre=s.num; // 方向需要调换一下,下上互换,左右互换
m[t.c_v]=num; //得到康拓值对应的存储路径结构体数组的下标
flag[t.c_v]=1; //标记
q.push(t);
}
swap(c_a[s.coor],c_a[s.coor+3]);
}
}
if(i==1) //左
{
if(s.coor!=1&&s.coor!=4&&s.coor!=7)
{
swap(c_a[s.coor],c_a[s.coor-1]);
t.c_v=cantor();
if(!flag[t.c_v])
{
t.coor=s.coor-1,t.num=++num;
memory[num].dir='r',memory[num].pre=s.num;
m[t.c_v]=num;
flag[t.c_v]=1;
q.push(t);
}
swap(c_a[s.coor],c_a[s.coor-1]);
}
}
if(i==2) //右
{
if(s.coor!=3&&s.coor!=6&&s.coor!=9)
{
swap(c_a[s.coor],c_a[s.coor+1]);
t.c_v=cantor();
if(!flag[t.c_v])
{
t.coor=s.coor+1,t.num=++num;
memory[num].dir='l',memory[num].pre=s.num;
m[t.c_v]=num;
flag[t.c_v]=1;
q.push(t);
}
swap(c_a[s.coor],c_a[s.coor+1]);
}
}
if(i==3)//上
{
if(s.coor>3)
{
swap(c_a[s.coor],c_a[s.coor-3]);
t.c_v=cantor();
if(!flag[t.c_v])
{
t.coor=s.coor-3,t.num=++num;
memory[num].dir='d',memory[num].pre=s.num;
m[t.c_v]=num;
flag[t.c_v]=1;
q.push(t);
}
swap(c_a[s.coor],c_a[s.coor-3]);
}
}
}
}
}
void deal_cin(int in)
{
if(str[in]=='x')
c_a[in]=9;
else
c_a[in]=str[in]-'0';
}
void print(int n) //回溯打印路径
{
if(!n) return ;
printf("%c",memory[n].dir);
print(memory[n].pre);
}
int main()
{
int i,j;
for(i=1; i<=9; i++)
c_a[i]=i;
bfs(); //打表
while(cin>>str[1])
{
deal_cin(1);
for(i=2; i<=9; i++)
cin>>str[i],deal_cin(i);
int ans=cantor();
if(flag[ans])
print(m[ans]);
else
printf("unsolvable");
puts("");
}
return 0;
}
康拓,逆康拓模板:
const int c_n=9;
bool c_flag[c_n+10];
int c_a[c_n+10],fact[15]= {0,1,1,2,6,24,120,720,5040,40320,362880};
int cantor() //康拓展开 c_a[1]到c_a[n]表示一个全排列,返回的ans表示康拓值(从0开始)
{
mem(c_flag,0);
int i,j,sum=0,ans=0,k;
for(i=1,k=c_n; i<=c_n; i++,k--)
{
sum=0;
for(j=c_a[i]-1; j>=1; j--)
{
if(!c_flag[j])
sum++;
}
c_flag[c_a[i]]=1,ans+=sum*fact[k];
}
return ans;
}
void inverse_cantor(int m) //康拓逆展开 传入的m是康拓值(从0开始),最终的c_a[1]到c_a[n]是m对应的排列
{
mem(c_flag,0);
int i,j,k,sum;
for(i=1,k=c_n; i<=c_n; i++,k--)
{
sum=m/fact[k]+1;
for(j=1; j<=c_n; j++)
{
if(!c_flag[j])
sum--;
if(!sum)
break;
}
c_a[i]=j,c_flag[j]=1,m%=fact[k];
}
}