「分治」黑白棋子的移动

本题为3月23日23上半学期集训每日一题中A题的题解

题面

题目描述

有2n个棋子(n≥4)排成一行,开始位置为白子全部在左边,黑子全部在右边,如下图为n=5的情形:

○○○○○●●●●●

移动棋子的规则是:每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能移成黑白相间的一行棋子。如n=5时,成为:

○●○●○●○●○●

任务:编程打印出移动过程。

输入

输入n。

输出

移动过程

样例输入

7

样例输出

step 0:ooooooo*******--
step 1:oooooo--******o*
step 2:oooooo******--o*
step 3:ooooo--*****o*o*
step 4:ooooo*****--o*o*
step 5:oooo--****o*o*o*
step 6:oooo****--o*o*o*
step 7:ooo--***o*o*o*o*
step 8:ooo*o**--*o*o*o*
step 9:o--*o**oo*o*o*o*
step10:o*o*o*--o*o*o*o*
step11:--o*o*o*o*o*o*o*

提示

注意格式

思路分析

在有样例的帮助下,此题不难分析.

本题让我们通过每次将相邻两个棋子和空位交换,来将原本一色连续的棋子变成两色相间的棋子.不难想到我们可以一组一组进行.我们先通过一系列操作,将两个异色棋子移动到最右端.由于每次只能动连续的两个棋子,所以这一步移动的两个异色棋子只有一种可能,那就是两个颜色交界处那两颗,所以这次移动即为: \(ooooooo*******-- -> oooooo--******o*\) .接着我们把最右边的两个黑色棋子和空位交换(做这步的原因你马上就知道了),这次移动为: \(oooooo--******o* -> oooooo******--o*\) .我们来观察一下移动的结果,此时最右边两个异色的棋子已经归位,而其余部分,正好形成了比原本棋子规模少一的连续棋子的形状,也就是说,我们接下来要求的问题就是,当前有 \(n-1\) 个棋子,将其变为两色相间的摆法.这和之前是同一个问题,但是规模减小了,我们目前完全可以按照同样的思路对这部分继续操作.虽然这不是严格意义上的分治法(严格意义的分治法是把一个问题不断划分成多个子问题,直到规模小到可以解决),但是思想上是近似的,即不断把问题分小,逐渐解决.

所以这题的基本思想就是,我们不断通过上面所说的那两步操作,将每组异色棋子归位.不过这里有个问题,当问题规模为2时,我们将一组异色棋子移动过去之后,会出现这样的情况: \(o--*o*o*o*o*o*o*\) ,此时我们无法通过任何操作将o复位(因为一次只能移动两个相邻棋子).这怎么办呢?索性我们有样例,仔细观察样例可知,当问题规模为4,我们将两个异色棋子放到右边之后,不再进行原本的分治策略,而是采用另一种方法(具体怎么操作看样例即可,本质上是在产生 \(*o\) ,来把最前面一部分变成 \(o*\) ,以使得最后可以移动棋子),所以当递归规模为4时,我们不进行第二步操作,而是直接按照样例进行特殊操作.

由于本题的分治法只是在单一减小问题规模,所以除了递归之外也可以用循环来解决.这边出于习惯我个人还是写的递归.

另外注意一下输出格式,步数要占2位.

参考代码

时间复杂度: \(O(N)\)

空间复杂度: \(O(N)\) (计入递归占用空间)

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

// 当前步数
int i = 0;

// 分治
void solve(string s, int n) {
    // 第一步,把o*和--交换(下标规律想不出来自己数一数应该也能发现)
    s[n - 1] = '-';
    s[n] = '-';
    s[2 * n] = 'o';
    s[2 * n + 1] = '*';
    cout << "step" << setw(2) << i++ << ":" << s << "\n";

    if (n > 4) {
        // 当规模大于4时进行正常操作,将最后的**和--交换
        s[n - 1] = '*';
        s[n] = '*';
        s[2 * n - 1] = '-';
        s[2 * n - 2] = '-';
        cout << "step" << setw(2) << i++ << ":" << s << "\n";
        
        // 此时左边成为一个问题规模缩小的相同问题,递归调用函数解决
        solve(s, n - 1);
    } else {
        // 特判,当规模为4时不进行正常的分治策略,下面的具体操作看样例即可
        s[7] = '-';
        s[8] = '-';
        s[3] = '*';
        s[4] = 'o';
        cout << "step" << setw(2) << i++ << ":" << s << "\n";
        s[1] = '-';
        s[2] = '-';
        s[7] = 'o';
        s[8] = 'o';
        cout << "step" << setw(2) << i++ << ":" << s << "\n";
        s[1] = '*';
        s[2] = 'o';
        s[6] = '-';
        s[7] = '-';
        cout << "step" << setw(2) << i++ << ":" << s << "\n";
        s[0] = '-';
        s[1] = '-';
        s[6] = 'o';
        s[7] = '*';
        cout << "step" << setw(2) << i++ << ":" << s << "\n";
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n;
    cin >> n;

    // 生成初始状态
    string s;
    for (int i = 0; i < n; i++) {
        s += "o";
    }
    for (int i = 0; i < n; i++) {
        s += "*";
    }
    s += "--"; // 因为每次移动两个,所以一定是要两个空位

    cout << "step" << setw(2) << i++ << ":" << s << "\n";
    solve(s, n);

    return 0;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

原文链接:https://www.cnblogs.com/geministar/p/zstu23_3_23_A.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:「分治」黑白棋子的移动 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • python银行卡号码校验Luhn模10算法

    Python银行卡号码校验Luhn模10算法 Luhn模10算法是一种用于验证银行卡号码是否有效的算法。本文将详细介绍如何使用Python实现Luhn模10算法,并提供两个示例说明。 Luhn模算法简介 Luhn模10算法是一种简单的算法,用于验证银行卡号码是否有效。它的基本思想是将银行卡号码的每个数字乘以不同的权重,然后将它们相加。如果相加的结果是10的倍…

    python 2023年5月14日
    00
  • C语言编程简单却重要的数据结构顺序表全面讲解

    C语言编程简单却重要的数据结构顺序表全面讲解 什么是顺序表? 顺序表是一种线性表,指的是一组有限元素的有限序列,其元素的逻辑顺序与它们在分配到的内存地址上的物理顺序相同或者等价。也就是说,顺序表中的元素按照其在内存中的位置依次存放。 顺序表的实现方式 顺序表的实现方式一般是使用数组,数组中的每一个元素对应着顺序表中的一个元素,位置相对应。 顺序表的优点 支持…

    数据结构 2023年5月17日
    00
  • 【ACM数论】和式变换技术,也许是最好的讲解之一

    在做数论题时,往往需要进行和式变换,然后变换成我们可以处理的和式,再针对和式做筛法、整除分块等操作。 本文将介绍一些常见的和式变换技术。 以下出现的概念大部分为个人总结,未必是学术界/竞赛界的统一说法,有不严谨的地方请谅解。 ? 作者:Eriktse? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流…

    算法与数据结构 2023年4月17日
    00
  • C++中的数组、链表与哈希表

    C++中的数组、链表与哈希表 数组 数组是一种数据结构,它存储的是一组相同类型的值。数组中每个元素的类型都是相同的,而且数组中的元素是按照一定的顺序排列的。C++中的数组是有序的,并且可以通过下标来访问数组中的元素。 数组的定义和初始化 在C++中定义数组的语法如下: type arr_name[arr_size]; 其中,type表示数组元素的类型,arr…

    数据结构 2023年5月17日
    00
  • python实现快速排序的示例(二分法思想)

    下面是详细讲解“Python实现快速排序的示例(二分法思想)”的完整攻略。 1. 什么是快速排序? 快速排序是一种常用的排序算法,它的基本想是通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有都要小,然后再按照此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达整个数据变成有序序列的目的。 2. 快速排序…

    python 2023年5月14日
    00
  • Java数据结构之实现哈希表的分离链接法

    Java数据结构之实现哈希表的分离链接法 哈希表是一种非常常用的数据结构,它将数据存储在一个数组中,每个数组元素都存储着链表中的一个节点,这样可以实现高效的数据存储和查找操作。在哈希表中,我们可以通过哈希函数将关键字映射到数组中的特定位置。 但是,当哈希表的负载因子过高时,就会造成哈希冲突,这意味着两个或更多的关键字映射到了同一个数组位置。一种常见的解决方案…

    数据结构 2023年5月17日
    00
  • C语言学习之链表的实现详解

    下面我将详细讲解“C语言学习之链表的实现详解”的完整攻略。 1. 链表的定义 链表是一种数据结构,它由一系列节点组成。每个节点由一个数据部分和一个指向下一个节点的地址部分组成。链表可以有多种形式,例如单向链表、双向链表、循环链表等。 2. 链表的实现 2.1. 单向链表 单向链表是最简单的链表形式,一个节点只包含一个指向下一个节点的指针。在C语言中,我们可以…

    数据结构 2023年5月17日
    00
  • Python实现遗传算法(二进制编码)求函数最优值方式

    下面是详细讲解“Python实现遗传算法(二进制编码)求函数最优值方式”的完整攻略,包括算法原理、Python实现和两个示例。 算法原理 遗传算法是一种基于自然选择和遗传机制的优化算法,其主要思想是通过模拟生物进化过程,寻找最优解。在二进制编码的遗传算法中,每个个体用一个二进制串表示,通过不断交叉、变异和选择操作,寻找最优解。 二进制编码的遗传算法的实现过程…

    python 2023年5月14日
    00
合作推广
合作推广
分享本页
返回顶部