UVa 1533 Moving Pegs 移动小球 BFS 二进制

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/qq_45523675/article/details/129290983
今日推荐