题意:一共有15个洞,成三角形分布,有一个洞是空的,其余都是有一个小球,小球可以穿过一个或多个小球到空洞里,被穿过的小球就被拿走(注意,穿过的小球必须至少一个),最后只剩一个小球,且在开始时候的空位上,问最少多少步,结果按字典序最小输出。
分析:此题不难想到用bfs搜索,数据储存是一个难点,数据成三角形,每一个点最多和6给点相邻。于是可以先列举这15个点的相邻节点,因为要按照字典序输出,所以方向依次是左上,右上,左,右,左下,右下。每次搜索小球位置,连续在一个方向上搜索两次,然后再判断在这个方向上是否有空地节点。
代码:
# include<iostream>
# include<cstdio>
# include<cmath>
# include<map>
# include<queue>
# include<string>
# include<string.h>
#include<set>
#include<list>
# include<algorithm>
using namespace std;
const int jump[15][6] = { {-1,-1,-1,-1,2,3},{-1,1,-1,3,4,5},{1,-1,2,-1,5,6},
{-1,2,-1,5,7,8},{2,3,4,6,8,9},{3,-1,5,-1,9,10},
{-1,4,-1,8,11,12},{4,5,7,9,12,13},{5,6,8,10,13,14},
{6,-1,9,-1,14,15},{-1,7,-1,12,-1,-1},{7,8,11,13,-1,-1},
{8,9,12,14,-1,-1},{9,10,13,15,-1,-1},{10,-1,14,-1,-1,-1}
};
int em;
struct node
{
int s;
int num;
int len;
int path[2][15];
};
void bfs() {
queue<node>q;
set<int>ms;
node temp;
temp.s = ((1 << 16) - 1) ^ (1 << (em - 1));//将有小球的节点用1表示
temp.num = 14;
temp.len = 0;
q.push(temp);
while (!q.empty()) {
node u = q.front(); q.pop();
for (int i = 0; i < 15; i++) {
if ((1 << i)&u.s) {//如果这个点有小球
for (int j = 0; j < 6; j++) {//寻找周围的小球
int cur = i;
node v = u;
if (jump[cur][j] != -1 && v.s&(1 << (jump[cur][j] - 1))) {//找到小球
while (cur >= 0 && (v.s&(1 << cur))) {//判断这个方向上是否有空洞
v.s ^= (1 << cur);//途中的小球变成空洞
v.num--;
cur = jump[cur][j] - 1;//同一个方向继续向下
}
if (cur < 0)continue;
v.s |= (1 << cur);//终点空洞加入小球
v.path[0][v.len] = i + 1;
v.path[1][v.len] = cur + 1;
v.len++;
v.num++;
if (!ms.count(v.s)) {//判重
if (v.num == 1 && v.s&(1 << (em - 1))) {//小球只剩下一个,且在开始的空洞中
cout << v.len << endl;
cout << v.path[0][0] << " " << v.path[1][0];
for (int h = 1; h < v.len; h++) {
cout << " " << v.path[0][h] << " " << v.path[1][h];
}
cout << endl;
return;
}
ms.insert(v.s);
q.push(v);
}
}
}
}
}
}
cout << "IMPOSSIBLE"<<endl;
}
int main() {
int kase; cin >> kase;
while (kase--) {
cin >> em;
bfs();
}
}