棋盘游戏(信息学奥赛一本通-T1451)

本文介绍了一种解决4x4棋盘上黑白棋移动问题的算法,利用BFS遍历所有可能的棋子移动序列,将棋盘状态转化为16位二进制数,通过异或运算实现棋子交换,最终找到从初始状态到目标状态的最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目描述】

在一个 4×4 的棋盘上有 8 个黑棋和 8 个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的。移动棋子的规则是交换相邻两个棋子。

给出一个初始棋盘和一个最终棋盘,请找出一个最短的移动序列使初始棋盘变为最终棋盘。

【输入】

前四行,每行 4 个数字(1 或者 0 ),描述了初始棋盘;

接着是一个空行;

第六到第九行,每行 4 个数字(1 或者 0),描述了最终棋盘。

【输出】

一行是一个整数 n,表示最少的移动步数。

【输入样例】

1111
0000
1110
0010

1010
0101
1010
0101

【输出样例】

4

思路:

本题是要从一个大状态变到另一个大状态,并且求其最小步数,很容易看出应用 BFS 来做

可以看出,每个单元非 0 即 1,共有 16 个单元,这样可以将每个棋盘当做一个 16 位的二进制数,将其转为十进制后再存储状态,最多有 2^16=65535 种状态

对于每种状态,由高到低枚举每个位置,计算该位置在棋盘上的坐标 (x,y),理论上来说,格子可与其上下左右相邻的四个格子任意进行交换,但显然,但采用异或运算交换两个相邻格子时,对每个格子四方向的枚举互换会造成大量的重复,因此,只要对每个格子从上到下,从左到右去尝试互换即可

【源程序】

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL multMod(LL a,LL b,LL mod){ a%=mod; b%=mod; LL res=0; while(b){if(b&1)res=(res+a)%mod; a=(a<<=1)%mod; b>>=1; } return res%mod;}
LL quickMultPowMod(LL a, LL b,LL mod){ LL res=1,k=a; while(b){if((b&1))res=multMod(res,k,mod)%mod; k=multMod(k,k,mod)%mod; b>>=1;} return res%mod;}
LL quickPowMod(LL a,LL b,LL mod){ LL res=1; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1; } return res; }
LL getInv(LL a,LL mod){ return quickPowMod(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-6;
const int MOD = 1000000000+7;
const int N = 1000+5;
const int dx[] = {0,0,-1,1,1,-1,1,1};
const int dy[] = {1,-1,0,0,-1,1,-1,1};
using namespace std;

struct Node {
    int val;
    int step;
    Node() {}
    Node(int val, int step) : val(val), step(step) {}
};
int vis[65536 + 10];
void BFS(Node st, Node ed) {
    memset(vis, 0, sizeof(vis));
    queue<Node> Q;
    Q.push(st);
    while (!Q.empty()) {
        Node now = Q.front();
        Q.pop();
        if (now.val == ed.val) {
            cout << now.step << endl;
            return;
        }

        int val = now.val;
        int step = now.step;
        for (int i = 15; i >= 0; i--) { //从高到低枚举每一位
            int x = (15 - i) / 4, y = (15 - i) % 4; //横纵坐标
            int nowPos = 1 << i; //当前位置代表权值
            int rightPos = 1 << (i - 1); //当前位置右方位置代表权值
            int downPos = 1 << (i - 4);  //当前位置下方位置代表权值
            if (y < 3 && ((val & nowPos) != (val & rightPos))) { //向右交换
                int nextVal = val ^ nowPos ^ rightPos; //交换后的状态
                if (!vis[nextVal]) {
                    vis[nextVal] = true;
                    Q.push(Node(nextVal, step + 1));
                }
            }
            if (x < 3 && ((val & nowPos) != (val & downPos))) { //向下交换
                int nextVal = val ^ nowPos ^ downPos; //交换后的状态
                if (!vis[nextVal]) {
                    vis[nextVal] = true;
                    Q.push(Node(nextVal, step + 1));
                }
            }
        }
    }
}
int main() {
    char ch;
    int st = 0, ed = 0;
    for (int i = 15; i >= 0; i--) {
        cin >> ch;
        if (ch == '1')
            st += 1 << i;
    }
    for (int i = 15; i >= 0; i--) {
        cin >> ch;
        if (ch == '1')
            ed += 1 << i;
    }
    if (st == ed)
        printf("0\n");
    else
        BFS(Node(st, 0), Node(ed, 0));
    return 0;
}

 

### 关于信息学奥赛一本 T1451 棋盘游戏的解法 #### 题目概述 题目描述了一个棋盘上的游戏,其中涉及到特定规则下的移动。为了找到解决方案,可以采用深度优先搜索(DFS)策略来遍历所有可能的状态空间树。 #### 使用 DFS 进行状态空间探索 过构建一个递归函数实现深搜算法,在每一步尝试所有的合法走步,并记录当前路径直到达到终止条件为止。当遇到重复访问过的节点或是不符合要求的情况时应回溯并继续其他分支的查找过程[^1]。 ```cpp #include <iostream> using namespace std; const int N = 8; bool visited[N][N]; int dx[] = {2, 1, -1, -2, -2, -1, 1, 2}; int dy[] = {1, 2, 2, 1, -1, -2, -2, -1}; void dfs(int x, int y) { if (/* 终止条件 */) { // 输出结果或计数器加一 return; } for (int i = 0; i < 8; ++i) { int nx = x + dx[i], ny = y + dy[i]; if (nx >= 0 && nx < N && ny >= 0 && ny < N && !visited[nx][ny]) { visited[nx][ny] = true; dfs(nx, ny); visited[nx][ny] = false; // 回溯操作 } } } ``` 此代码片段展示了如何利用八个方向数组模拟马在国际象棋中的跳跃动作,并结合`dfs()`方法完成整个棋局可能性枚举的任务。 #### 处理同行同列及对角线冲突 对于某些情况下需要排除掉位于同一行列以及主副两条斜线上方格的情形,则可以在每次扩展新位置之前加入额外判断逻辑以跳过这些非法选项[^2]: ```cpp if ((abs(x1-x2)==abs(y1-y2)) || (x1==x2)|| (y1==y2)){ continue; // 如果在同一行/列 或 对角线上则不计入下一步的选择之中 } ``` 上述代码段用于检测两个坐标点之间是否存在直线连接关系,从而决定是否允许该次转移发生。 #### 应用 BFS 寻找最优解 考虑到可能存在多条可行路线往终点,而题目往往追求的是最少步数方案;此时引入队列结构支持下的广度优先搜索能够有效地保证最先抵达目的地的就是所求答案之一[^3]。 ```cpp queue<pair<int,int>> q; q.push({startX,startY}); while(!q.empty()){ auto [cur_x, cur_y]=q.front();q.pop(); if(/* 到达目标 */){ break; } for(auto& dir:{...} /* 定义好各个行走模式*/ ){ int next_x=cur_x+dir.first,next_y=cur_y+dir.second; if(/* 合法性验证 */){ ... } } } ``` 这段伪代码框架说明了怎样借助FIFO性质的数据容器配合循环迭代机制逐步向外层扩散直至触及边界或满足结束标志位的要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值