题目链接:Moving Pegs
题目描述:
存在一个如图所示的金字塔形状的网格,其中一个结点为空(图中白色结点),其余结点均放置一个石头(图中黑色结点),空节点的位置会通过输入告诉你,你现在每次可以进行一次移动操作,移动操作可以将一个石头沿直线移动到一个空白,移动后,经过的石头同样会被拿掉(类似于跳棋的规则)例如你可以将 14 14 14移动到 5 5 5这样 9 , 14 9,14 9,14均变成空白,而 5 5 5位置有一个石头,需要注意的一点是:你不能将一个石头直接移动到空白中,例如你不能将 9 9 9移动到 5 5 5。你需要花费尽可能少的步数使得只剩下一个石头,同时这个石头处在的位置为一开始空白的位置,例如上图你需要变成只有 5 5 5位置存在一个石头。输出路径。
题解:
本题可以用一个 15 15 15位的二进制来表示每个节点是否存在石头,可以知道的是,一个节点能够移动的方向最多有六个:左上、右上、左、右、左下、右下。那么我们可以实现记录每个节点能够移动到的相邻结点的位置,不能移动到的地方用 − 1 -1 −1表示,这样在转移的时候只需要一直沿着某个方向移动,直到遇到空的位置。同时使用一个数组保存移动操作,打印路径的时候只需要做到倒序输出即可。
代码:
#include <bits/stdc++.h>
const int INF = 0x3f3f3f3f;
const int NUM_DIRECTION = 6;
const int MAXN = 15;
using namespace std;
int T, n;
int dis[1 << MAXN];
// 每个结点最多可以有六个方向:左上、右上、左、右,左下、右下
// 结点的编号从零开始
int to[][NUM_DIRECTION] = {
{
-1, -1, -1, -1, 1, 2},
{
-1, 0, -1, 2, 3, 4},
{
0, -1, 1, -1, 4, 5},
{
-1, 1, -1, 4, 6, 7},
{
1, 2, 3, 5, 7, 8},
{
2, -1, 4, -1, 8, 9},
{
-1, 3, -1, 7, 10, 11},
{
3, 4, 6, 8, 11, 12},
{
4, 5, 7, 9, 12, 13},
{
5, -1, 8, -1, 13, 14},
{
-1, 6, -1, 11, -1, -1},
{
6, 7, 10, 12, -1, -1},
{
7, 8, 11, 13, -1, -1},
{
8, 9, 12, 14, -1, -1},
{
9, -1, 13, -1, -1, -1}
};
struct Path
{
int from, to, lastState;
Path() {
}
Path(int from, int to, int lastState) : from(from), to(to), lastState(lastState) {
}
};
Path path[1 << MAXN];
// 判断状态s的i位置是否为空
bool empty(int s, int i) {
return (s & (1 << i)) == 0; }
// 返回一个状态有几个1
int countOne(int s)
{
int cnt = 0;
while (s) {
if (s & 1) {
cnt++; }
s >>= 1;
}
return cnt;
}
void print(int now, char ch)
{
if (path[now].to == -1) {
return; }
print(path[now].lastState, ' ');
cout << path[now].from << " " << path[now].to;
if (ch != 0) {
cout << ch; }
}
void bfs()
{
queue<int> q;
int initialState = ((1 << 15) - 1) ^ (1 << n);
q.push(initialState);
dis[initialState] = 0;
path[initialState] = {
-1, -1, -1};
int now, newState;
while (!q.empty()) {
now = q.front(); q.pop();
if (countOne(now) == 1 && !empty(now, n))) {
cout << dis[now] << endl;
print(now, 0);
cout << endl;
return;
}
for (int u = 0; u < MAXN; u++) {
if (empty(now, u)) {
continue; }
for (int j = 0; j < NUM_DIRECTION; j++) {
int v = to[u][j];
if (v == -1 || empty(now, v)) {
continue; } // 必须至少跳一个有石头的位置
newState = now ^ (1 << u);
while (v != -1 && !empty(newState, v)) {
// 沿着一个方向走到第一个为空的地方
newState ^= 1 << v;
v = to[v][j];
}
if (v == -1) {
continue; }
newState ^= 1 << v;
if (dis[newState] != -1) {
continue; }
path[newState].from = u + 1; path[newState].to = v + 1; path[newState].lastState = now;
dis[newState] = dis[now] + 1;
q.push(newState);
}
}
}
cout << "IMPOSSIBLE" << endl;
}
int main()
{
cin >> T;
while (T--) {
memset(dis, -1, sizeof(dis));
cin >> n; n--;
bfs();
}
return 0;
}