北交入学训练之变种汉诺塔问题

Tower of Hanoi

成绩: 5 / 折扣: 0.8

题面描述 

变种汉诺塔问题和普通汉诺塔问题略有不同,规则描述如下: 

1. 有三根柱子,在最左侧柱子上放置着若干圆盘。与传统汉诺塔不同的是,其中存在部分大小相同的圆盘。 

2. 要求包括初始状态在内,每个圆盘上方放置的圆盘不得大于该圆盘,即圆盘上方只能放置小于自己或和自己相同大小的圆盘。 

3. 每次移动只能将某柱子最顶部的一个圆盘移动到另一柱子的最顶部。 

4. 需要注意的是,大小相同的圆盘具有的其他特征是不一样的,例如不同颜色。 

最后需要保证 2 号柱子上的圆盘排列顺序,和开始时的 0 号柱子上的顺序完全相同。 

求将初态 0 号柱子上的所有圆盘全部移到 2 号柱子上最优策略的步数 l 对 m 取模后的值。  

输入数据 

对于每组数据: 

第一行有一个整数 t (1 ≤ t ≤ 100 ) ,表示有 t 组数据。

第一行包括 2 个数字 n,m (1≤n≤15000, 1≤m≤1000000) ,其中 n 代表圆盘种类的个数; 

第二行包括 n 个数字 a1, … , an (1 ≤ ai≤ 99 ),其中 ai 代表大小为 i 的圆盘个数。 

输出数据 

对于每组数据,输出一行,若最优策略的步数为 l ,则输出 l mod m 。 

样例输入 

2
2 1000
1 2
3 1000
1 2 3 

样例输出 

7
21


思想:主要是考虑不同情况下的最少移动次数,我是递归的思维想,然后用递推实现。以第二个例子为例。

首先,把每一类圆盘看做一类,这时有三类,个数分别为1,2,3,记作arr[1]=1,arr[2]=2,arr[3]=3。

第二步,要考虑到因为有相同大小盘子的存在,移动盘子中间可以不保证顺序,只要最终顺序正确即可。第二个是整体的思想,把整个问题想成上面n-1类盘子与最后一类盘子的问题,对于我们这个例子,先想成上面两类盘子是一块,最下面一类盘子是一块,即可得出unorder[3]=onorder[2]*2+arr[3],而对于unorder[2],同理想成上面一类是一块,下面一类是一块,unorder[2]=unorder[1]*2+arr[2],而最后unorder[1],不论arr[1]个数是多少,都只需arr[1]次,即unorder[1]=arr[1]。

第三步,最终我们的目的是移动后有序,而对于有序的移动,有两种情况,第一种是,最下面的一类盘子数量只有一个,这种情况下最优的操作是,上面的其他盘子移动两次,最下面的盘子移动一次,即order[n]=unorder[n-1]*2+1,这里使用无序移动,是因为无序偶次就是有序。第二种情况是,最下面的盘子数量有n个(n>1),这种情况下最优操作是上面的剩余盘子2次无序移动和一次有序移动,下面的这类盘子移动2*arr[n]次,即,order[n]=2*unorder[n-1]+2*arr[n]+order[n-1],而对于order[1]一定是2*arr[1]-1次(可以自己动手算下,就是最后一个盘子只移动一次,其余盘子移动两次,即可有序)。

AC代码如下:

#include <iostream>
#include <stdio.h>
using namespace std;
int unorder[15001], order[15001], arr[15001];
int main() {
    int T, n, m;
    scanf("%d", &T);
    for (int i=1;i<=T;i++) {
        scanf("%d %d", &n, &m);
        for (int j=1;j<=n;j++) {
            scanf("%d", &arr[j]);
        }
        unorder[1] = arr[1];
        for (int j=2;j<=n;j++) {
            unorder[j] = ((2*unorder[j-1])%m+arr[j])%m;
        }
        order[1] = 2*arr[1]-1;
        for (int j=2;j<=n;j++) {
            if (arr[j]==1) {
                order[j] = ((2*unorder[j-1])%m+1)%m;
            }else{
                order[j] = ((2*unorder[j-1])%m+2*arr[j]+order[j-1])%m;
            }
        }
        printf("%d\n", order[n]);
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值