• 【例7.6】黑白棋子的移动


    1327:【例7.6】黑白棋子的移动


    时间限制: 1000 ms         内存限制: 65536 KB
    提交数: 1475     通过数: 617 

    【题目描述】

    有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*

    例题不怎么详的解:
    经过对题目的观察,发现题目规律,当n>4时,总是将中间的一对两个不同的棋子移到最右边的空位,然后再把最左边的黑棋子移到中间,如此往复,直到剩下四对黑白棋没有移动。
    不过我们发现,这4对棋子的移法是一成不变的,不受其他棋子影响。
    因此,这个条件就可以作为递归边界,得出最后5步的走法。
    看输出样例,可以得到剩下4步的普适移法,即:
    1. 先将第4、5个棋子移到右边空位。
    2. 将第8、9位置的棋子移到先前的空位。
    3. 将2、3位置的棋子移到先前的空位。
    4. 将7、8位置的棋子移到先前的空位。
    5. 将5、6位置的棋子移到之前的空位。

    惊讶的发现,每次移动都是将某对棋子移到两个空位上,那么n>4时呢?

    同样的,也是将中间的一对移到空位上,再将最右边的一对黑棋移到先前的空位上,如此往复。

    恍然大悟。

    算法分析:

    本题的关键点就在于这个空位的问题,抓住了这个点,本题也就迎刃而解,因此我们需要设置一个代表空位起始位置的变量sp。

    另一个要点,也是代码核心组成部分,就是移动棋子这一块了,通过上面的分析,我们得出普适的两步:

    1. 把中间位置的一对黑白棋移到最右边。
    2. 把最左边的一对黑棋移到中间的空位上。

    这就是分治的思想,将这个问题分解成了一个个子问题,这些问题就是要进行上面两步求解。

    得出核心代码块:

    for(j=0;j<2;j++)
    	{
    		a[sp+j]=a[k+j-1];
    		a[k+j-1]='-';
    	}
    	sp=k-1;
    

     

    接着把前面提到的n=4时的情况写出:

    if(n==4)
    	{
    		move(4);move(8);move(2);move(7);move(1);
    	}
    

      

    还有n>4的情况:

    move(n);move(n*2-1);mv(n-1);
    

      

    样例代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    char a[101];
    int sp,step=0,n;
    void print()//输出
    {
    	printf("step%2d:",step);
    	for(int i=0;i<2*n+2;i++) printf("%c",a[i]);
    	printf("
    ");
    	step++;
    }
    void move(int k)//分治:移动
    {
    	int j;
    	for(j=0;j<2;j++)
    	{
    		a[sp+j]=a[k+j-1];
    		a[k+j-1]='-';
    	}
    	sp=k-1;
    	print();
    }
    void mv(int n)//递归求解
    {
    	int i,k;
    	if(n==4)
    	{
    		move(4);move(8);move(2);move(7);move(1);
    	}
    	else
    	{
    		move(n);move(n*2-1);mv(n-1);
    	}
    }
    int main()
    {
    	int i;
    	scanf("%d",&n);
    	for(i=0;i<n;i++) a[i]='o';
    	for(i=n;i<2*n;i++) a[i]='*';
    	a[2*n]='-';a[2*n+1]='-';
    	sp=2*n;//初始化
    	print();
    	mv(n);
    	return 0;
    }
    

      

  • 相关阅读:
    剑指offer--38.左旋转字符串
    剑指offer--37.和为S的两个数字
    剑指offer--35.数组中只出现一次的数字
    剑指offer--34.数字在排序数组中出现的次数
    剑指offer--33.丑数
    剑指offer--36.整数中1出现的次数(从1到n整数中1出现的次数)
    剑指offer--32.把数组排成最小的数
    剑指offer--31.二叉树中和为某一值的路径
    剑指offer--30.二叉搜索树的后序遍历序列
    剑指offer--29.从上往下打印二叉树
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/10387968.html
Copyright © 2020-2023  润新知