HDU 1043 Eight(双向BFS+康托展开)

时间:2023-03-08 16:27:24

http://acm.hdu.edu.cn/showproblem.php?pid=1043

题意:给出一个八数码,求出到达指定状态的路径。

思路:路径寻找问题。在这道题里用到的知识点挺多的。第一次用双向BFS来做。

①双向BFS

在单向BFS的基础上,多建一个从终止状态开始搜索的队列,当然这个时候需要两个vis[]辅助数组,分别记录两个队列的访问情况,当两个队列相遇时即可终止循环。

②康托展开

X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,其中a[i]为当前未出现的元素中是排在第几个(从0开始)。这就是康托展开。

例:321的康托展开为:2*2!+1*1!+0*0!;

(数字3后比3小的数有2个,数字2后比2小的数有1个...依次类推)

③逆序数判断

每次交换两个数字逆序数的奇偶性不变,这个线代里有说过的,这样就可以根据最后状态的逆序数奇偶性来判断当前所给状态能给转换成功。不然的话好像是会超时的。

 #include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std; const int maxn = ;
const char* DIR1 = "udlr";
const char* DIR2 = "durl"; int fac[] = { , , , , , , , , }; //阶乘值 int x; //x的位置 int dir[] = { -, , -, }; int vis1[maxn];
int vis2[maxn]; char ss[] = "";
char str[]; struct Node
{
int x;
char str[];
}; struct Step
{
int cnt;
char dir;
}pre[maxn]; //记录路径 int sequence(char a[]) //计算康托展开值
{
int sum = ;
for (int i = ; i < ; i++)
{
int k = ;
for (int j = i + ; j < ; j++)
{
if (a[j] < a[i])
k++;
}
sum += fac[ - - i] * k;
}
return sum;
} void printfff(int x) //追溯到起点输出路径
{
if (pre[x].cnt == -) return;
printfff(pre[x].cnt);
cout << pre[x].dir;
} int judge(int x, int i) //判断是否越界
{
int xx = x / ; //行
int yy = x % ; //列
if (i == )
{
int yyy = yy + ;
if (yyy > ) return ;
}
if (i == )
{
int yyy = yy - ;
if (yyy < ) return ;
}
if (i == )
{
int xxx = xx + ;
if (xxx>) return ;
}
if (i == )
{
int xxx = xx - ;
if (xxx < ) return ;
}
return ;
} void bfs()
{
memset(vis1, , sizeof(vis1));
memset(vis2, , sizeof(vis2)); queue<Node> q1, q2;
Node p1, p2; int count = ;
strcpy(p1.str, str); //初始
p1.x = x; //初始x的位置
//cout << p1.str << endl;
strcpy(p2.str, ss); //终极
p2.x = ; //终极x的位置
//cout << p2.str << endl;
q1.push(p1);
q2.push(p2);
vis1[sequence(str)] = ;
vis2[sequence(ss)] = ;
pre[].cnt = -; //起点标记
pre[].cnt = -; //终点标记
while (!q1.empty() && !q2.empty())
{
Node u = q1.front();
q1.pop();
int p = sequence(u.str);
if (vis2[p]) //找到目标状态
{
printfff(vis1[p]);
int k = vis2[p];
while (pre[k].cnt != -)
{
cout << pre[k].dir;
k = pre[k].cnt;
}
cout << endl;
return;
}
else //未找到目标状态
{
Node u2;
for (int i = ; i < ; i++)
{
u2 = u;
if (!judge(u.x, i)) continue;
int xx = u.x + dir[i]; //x的新地址
swap(u2.str[u.x], u2.str[xx]);
u2.x = xx;
int v = sequence(u2.str);
if (vis1[v]) continue; //已访问
vis1[v] = ++count;
pre[count].dir = DIR1[i]; //记录方向
pre[count].cnt = vis1[p];
q1.push(u2);
}
} u = q2.front();
q2.pop();
p = sequence(u.str);
if (vis1[p])
{
printfff(vis1[p]);
int k = vis2[p];
while (pre[k].cnt != -)
{
cout << pre[k].dir;
k = pre[k].cnt;
}
cout << endl;
return;
}
else //未找到目标状态
{
Node u2;
for (int i = ; i < ; i++)
{
u2 = u;
if (!judge(u.x, i)) continue;
int xx = u.x + dir[i]; //x的新地址
swap(u2.str[u.x], u2.str[xx]);
u2.x = xx;
int v = sequence(u2.str);
if (vis2[v]) continue; //已访问
vis2[v] = ++count;
pre[count].dir = DIR2[i]; //记录方向
pre[count].cnt = vis2[p];
q2.push(u2);
}
}
}
cout << "unsolvable" << endl;
} int main()
{
//freopen("D:\\txt.txt", "r", stdin);
char s[];
while (gets(s))
{
int k = ;
int t = ;
for (int i = ; s[i] != NULL; i++)
{
if (s[i] == 'x') { str[k] = ''; x = k; }
else if (s[i] >= '' && s[i] <= '') str[k] = s[i];
else continue;
k++;
}
str[k] = '\0';
//int result = sequence(ss);
//cout << result << endl;
//cout << str << endl << ss << endl << x << endl; for (int i = ; i<; i++) //逆序数判断是否可行
{
if (str[i] == '')continue;
for (int j = ; j<i; j++)
{
if (str[j] == '')continue;
if (str[j]>str[i])t++;
}
}
if (t & ) cout << "unsolvable" << endl;
else bfs();
}
return ;
}