康拓展开原理:
来自:http://blog.csdn.net/zhongkeli/article/details/6966805
康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! 其中,ai为当前未出现的元素中是排在第几个(从0开始)。
这个公式可能看着让人头大,最好举个例子来说明一下。例如,有一个数组 s = ["A", "B", "C", "D"],它的一个排列 s1 = ["D", "B", "A", "C"],现在要把 s1 映射成 X。n 指的是数组的长度,也就是4,所以
X(s1) = a4*3! + a3*2! + a2*1! + a1*0!
关键问题是 a4、a3、a2 和 a1 等于啥?
a4 = "D" 这个元素在子数组 ["D", "B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,"D"是第3大的元素,所以 a4 = 3。
a3 = "B" 这个元素在子数组 ["B", "A", "C"] 中是第几大的元素。"A"是第0大的元素,"B"是第1大的元素,"C" 是第2大的元素,所以 a3 = 1。
a2 = "A" 这个元素在子数组 ["A", "C"] 中是第几大的元素。"A"是第0大的元素,"C"是第1大的元素,所以 a2 = 0。
a1 = "C" 这个元素在子数组 ["C"] 中是第几大的元素。"C" 是第0大的元素,所以 a1 = 0。(因为子数组只有1个元素,所以a1总是为0)
所以,X(s1) = 3*3! + 1*2! + 0*1! + 0*0! = 20
具体实现如下:
A B C | 0
A C B | 1
B A C | 2
B C A | 3
C A B | 4
C B A | 5
首先我们采用康托展开就是可以将任何一个排列转换成唯一的数字。因为我们要标记这个序列是否访问过的话,是不可以吧一个字符串放进数组的,必须运用康托展开将这个字符串序列转换成唯一的数字,标记才有意义。
其次就是预处理。
根本就是将所有的始态的换成统一的形式,将对应位置的数字分别对应为12345678,举一个例子,如下:
始态13542687 目标态12345678
转化方式为
for (int i=0; i<8; i++)//得到对应关系
{
nn[ch[i]-'0']=i+1;
}
得到之后,按照对应关系将原来的目标态转化新的目标态
for (int i=0;i<8;i++)//将目标态按照转换关系变成过程态的对应的目标态
{
str[i]=nn[str[i]-'0']+'0';
}
转换完成之后,按照这个过程态对目标态进行搜索。这个初始态的所有变换情况已经预处理完成。
详见题目。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1430
魔板
1 2 3 4
8 7 6 5
对于魔板,可施加三种不同的操作,具体操作方法如下:
A: 上下两行互换,如上图可变换为状态87654321
B: 每行同时循环右移一格,如上图可变换为41236785
C: 中间4个方块顺时针旋转一格,如上图可变换为17245368
给你魔板的初始状态与目标状态,请给出由初态到目态变换数最少的变换步骤,若有多种变换方案则取字典序最小的那种。
12345678 17245368 12345678 82754631
C AC
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
struct node
{
string st;//表示当前的状态
string step;//变化的过程
} s,ss;
int num[15];//阶乘
int vis[400000];
string Count[400000];
void fac()
{
num[0]=1;
num[1]=1;
for (int i=1; i<10; i++)
num[i]=num[i-1]*i;
}
int Cantor(string &ch)
{
int ans=0;
for (int i=0; i<8; i++)
{
int t=0;
for (int j=i+1; j<8; j++)
{
if (ch[i]>ch[j])
t++;
}
ans+=t*num[8-i-1];
}
return ans;
}
void fun_A(string &ch)
{
for (int i=0; i<4; i++)
swap(ch[i],ch[8-i-1]);
}
void fun_B(string &ch)
{
int t=ch[3];
//ch[0]=ch[3];
for (int i=3; i>=1; i--)
ch[i]=ch[i-1];
ch[0]=t;
int tt=ch[4];
for (int i=4; i<=6; i++)
ch[i]=ch[i+1];
ch[7]=tt;
}
void fun_C(string &ch)
{
int t=ch[6];
ch[6]=ch[5];
ch[5]=ch[2];
ch[2]=ch[1];
//ch[1]=ch[6];
ch[1]=t;
}
void bfs()
{
queue<node>q,qq;
s.st="12345678";
s.step="";
//vis[6909000]= {0};
memset(vis,0,sizeof(vis));
q.push(s);
vis[Cantor(s.st)]=1;
while (!q.empty())
{
s=q.front();
q.pop();
//int cnt= Cantor(s.st);
//count[cnt]=s.st;
ss=s;
//cout<<s.st<<" "<<s.step<<endl;
fun_A(ss.st);
//cout<<ss.st<<endl;
int cnt= Cantor(ss.st);
if (!vis[cnt])
{
ss.step+="A";
Count[cnt]=ss.step;
q.push(ss);
vis[cnt]=1;
}
ss=s;
fun_B(ss.st);
//cout<<ss.st<<endl;
cnt= Cantor(ss.st);
if (!vis[cnt])
{
ss.step+="B";
Count[cnt]=ss.step;
q.push(ss);
vis[cnt]=1;
}
ss=s;
fun_C(ss.st);
//cout<<ss.st<<endl;
cnt= Cantor(ss.st);
if (!vis[cnt])
{
ss.step+="C";
Count[cnt]=ss.step;
q.push(ss);
vis[cnt]=1;
}
}
//printf ("\n");
}
int main()
{
string ch;
string str;
fac();
bfs();
while (cin>>ch)
{
//cout<<ch<<endl;
int nn[15];
for (int i=0; i<8; i++)//得到对应关系
{
nn[ch[i]-'0']=i+1;
}
cin>>str;
for (int i=0;i<8;i++)//将目标态按照转换关系变成过程态的对应的目标态
{
str[i]=nn[str[i]-'0']+'0';
}
//cout<<str<<endl;
int ann=Cantor(str);
cout<<Count[ann]<<endl;
}
return 0;
}