bfs 剪枝要点
visit
数组 hash函数(康托展开)记忆化
bfs 打表存储
所有可达路径优先队列
periority queue多点同时bfs
反向bfs
+ bfs 打表存储所有路径
stl + 正向bfs
9! 一共有 362880 种可能,10e5 数据规模并不是非常大,所以我考虑用 map<string , bool>
来作为visit。map 的访问和插入都是 log n
所以对于每一个输入的序列,时间复杂度都是 n*log n
log 362880
约等于 20 ,所以总的时间复杂度等于 36288020 = 7257600 , 71e6,对单个testcase 是可接受的。
如果再考虑 有多个 test case
,13个testcase
就会达到 1e8
的时间复杂度,显然是不可接受的。
正向bfs 加 hash判重
此处要引入一个定理 康托展开和逆康托展开
康托展开是一个全排列到一个自然数的双射,常用于构建hash表时的空间压缩。设有n个数(1,2,3,4,…,n),可以有组成不同(n!种)的排列组合,康托展开表示的就是是当前排列组合在n个不同元素的全排列中的名次。
(转自此处)
显然如果我们此处使用康托展开作 hash 映射,visit 数组就可以从 一个 map
退化成一个 一维数组
时间复杂度是 n
同上
但是这么写的话,会有很多次重复的操作
。所以testcase过大,也会 t。
反向bfs + 记忆化 + hash 判重
由于树的结构特点,一个子节点一定只有一个父节点。
所以可以直接从根节点开始便利,使用链式前向星的思想,对每个节点,记录该节点的父节点。
然后只需要进行一次bfs 搜索即可。
代码如下:
#include<iostream>
#include<queue>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
struct node
{
int pre;
int op;
string s;
int x;
int y;
};
node path[400000];
int vis[400000];
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
char ss[4] = { 'u','d','l','r'};
int fac[] = {1,1,2,6,24,120,720,5040,40320,362880}; //i的阶乘为fac[i]
int Cantor(string s){
int sum = 0;
for(int i = 0; i < 9; i++){
int num = 0;
for(int j = i+1; j < 9; j++){
if(s[j] < s[i])
num++;//确定当前元素i在未出现的元素中是第几个(从小到大)
}
sum += fac[8-i] * num;
}
return sum;
}
void bfs()
{
int counts =0;
path[counts].pre = -1;
path[counts].s = "123456780";
path[counts].x=2;
path[counts].y=2;
queue<int> qu;
qu.push(counts);
int visn;
visn = Cantor(path[counts].s);
vis[visn] =counts;
counts++;
while(!qu.empty())
{
int f = qu.front();
qu.pop();
int x = path[f].x;
int y = path[f].y;
char tp;
for(int i=0;i<4;i++)
{
int px = x+dir[i][0];
int py = y+dir[i][1];
if(px<0||px>2||py<0||py>2)continue;
tp = path[f].s[px*3+py]; // 开始转换
path[f].s[px*3+py] = path[f].s[x*3+y];
path[f].s[x*3+y] = tp;
visn = Cantor(path[f].s); //判断是否出现过
if(vis[visn]!=-1)
{
tp = path[f].s[px*3+py]; // 回溯
path[f].s[px*3+py] = path[f].s[x*3+y];
path[f].s[x*3+y] = tp;
continue;
}
vis[visn] =counts;
path[counts].op = i;
path[counts].pre = f;
path[counts].s = path[f].s;
path[counts].x = px;
path[counts].y = py;
qu.push(counts);
counts++;
tp = path[f].s[px*3+py]; //回溯
path[f].s[px*3+py] = path[f].s[x*3+y];
path[f].s[x*3+y] = tp;
}
}
}
int main()
{
for(int i=0;i<400000;i++)vis[i]=-1;
bfs();
char st1[30];
string st;
ios::sync_with_stdio(false);
vector<int> re;
while(cin.getline(st1,25))
{
st="";
re.clear();
for(int i=0;i<17;i+=2)
{
if(st1[i]=='x')st = st+'0';
else st= st+st1[i];
}
int viss;
viss = Cantor(st);
if(vis[viss]!=-1)
{
int flag=vis[viss];
while(path[flag].pre!=-1)
{
cout<<ss[path[flag].op];
flag = path[flag].pre;
}cout<<endl;
}
else cout<<"unsolvable"<<endl;
}
return 0;
}