[luogu p1259] 黑白棋子的移动

传送门

黑白棋子的移动

题目描述

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

○○○○○●●●●●

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

○●○●○●○●○●

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

输入输出格式

输入格式

一个整数n(n<=100)

输出格式

若干行,表示初始状态和每次移动的状态,用"o"表示白子,"*"表示黑子,"-"表示空行。

输入输出样例

输入 #1

7

输出 #1

ooooooo*******--
oooooo--******o*
oooooo******--o*
ooooo--*****o*o*
ooooo*****--o*o*
oooo--****o*o*o*
oooo****--o*o*o*
ooo--***o*o*o*o*
ooo*o**--*o*o*o*
o--*o**oo*o*o*o*
o*o*o*--o*o*o*o*
--o*o*o*o*o*o*o*

分析

这道题我看了半天题面就是搞不懂题意,没办法只好观察样例。

乍一看没啥规律,但是如果你仔细观察,可能会发现,样例中的第三行,是这样的:

oooooo******--o*

可以看到,前面一部分形成了六白六黑两个空的情形。这不就是\(n = 6\)的情况吗?

再往下两行(样例中的第五行)则是这样的:

ooooo*****--o*o*

没错,前面又形成了\(n = 5\)的情况。

再往下两行呢?(样例中的第七行):

oooo****--o*o*o*

前面又形成了\(n = 4\)的情况。

而题目中的数据范围是\(4 \le n \le 100\),也就是\(n\)最小取4。

那么我们就差不多明白了,这是一道分治,不断的分解情况,将\(n = k\)的情况转化为\(n = k - 1\)的情况,一直到\(n = 4\)

那么这是怎么转化的呢?还是一样观察样例,我们看看是怎么从\(n = 7\)的情况转化为\(n = 6\)的情况的。

ooooooo*******--
      ||      ||
oooooo--******o*
      ||    ||
oooooo******--o*

看起来是把前边的中间的一黑一白移到最后的两个空位,然后再将原来排列的最后两颗星星补上前面的空位。(移动的部分已经用||标出。)

那么\(n = 6\)\(n = 5\)呢?

oooooo******--o*
     ||     ||
ooooo--*****o*o*
     ||   ||
ooooo*****--o*o*

没错,还是这个套路。

转化的方法我们知道了,那么接下来再看\(n = 4\)是怎么处理的吧。

oooo****--o*o*o*
   ||   ||
ooo--***o*o*o*o*
   ||  ||
ooo*o**--*o*o*o*
 ||    ||
o--*o**oo*o*o*o*
 ||   ||
o*o*o*--o*o*o*o*
||    ||
--o*o*o*o*o*o*o*

这个看起来没什么规律,应该是\(n = 4\)的基本情况了。在代码中我们直接按照这种方法,照葫芦画瓢式移动就行。

上代码啦!

代码

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-04-28 11:24:59 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-04-28 13:32:37
 */
#include <iostream>
#include <cstdio>

const int maxn = 105;

int n;
char s[maxn * 2 + 5];
int empty_idx;//empty_idx记录的是两个空位中的前面那个空位的下标。

void print() {//输出函数
    for(int i = 1; i <= 2 * n + 2; i++)
        std :: cout << s[i];
    std :: cout << std :: endl;
    return ;
}

void init() {//初始化函数
    for(int i = 1; i <= n; i++) s[i] = 'o';
    for(int i = n + 1; i <= 2 * n; i++) s[i] = '*';
    for(int i = 2 * n + 1; i <= 2 * n + 2; i++) s[i] = '-';
    empty_idx = 2 * n + 1;
    print();//别忘了最初情况也需要输出
    return ;
}

void move(int x) {//移动函数
    s[empty_idx] = s[x];
    s[empty_idx + 1] = s[x + 1];
    s[x] = s[x + 1] = '-';
    empty_idx = x;
    print();
}
//move(x)的效果:将第x个棋与第x + 1个棋一起分别移到两个空位上

void solve(int k) {//分治函数
    if(k == 4) {
        move(4);
        move(8);
        move(2);
        move(7);
        move(1);
        //k=4的情况就是完全照葫芦画瓢咯。
    } else {
        move(k);//先把中间的移最后
        move(2 * k - 1);//再移后边的两个*移中间
        solve(k - 1);//分治
    }
}

int main() {
    std :: cin >> n;//读入
    init();//初始化
    solve(n);//调用分治
    return 0;
}

评测结果

AC 100:R33173543

posted @ 2020-04-29 10:32  dbxxx  阅读(358)  评论(0编辑  收藏  举报