2018年全国多校算法寒假训练营练习比赛(第一场) - D - N阶汉诺塔变形(模拟)

时间:2021-01-14 09:54:59

链接:https://www.nowcoder.net/acm/contest/67/D
来源:牛客网

相信大家都知道汉诺塔问题。那么现在对汉诺塔问题做一些限制,成为一个新的玩法。
    在一个底座上,从左到右有三个分别命名为A、B和C的塔座,有n个大小不一的圆盘。这些圆盘一开始,从小到大按顺序叠加在塔座A上,形成一座上小下大的塔,塔座B和C为空。我们将n个圆盘,从小到大编号为1~n。现要求将塔座A上的n个圆盘移至塔座C上并仍按照同样的顺序叠排,圆盘移动时必须遵循以下规则:
    (1)每次只能将一个圆盘从一个塔座移动到相邻的塔座上
    (2)所有圆盘可以叠在A、B和C中的任一塔座上
    (3)任何时刻都不能将一个较大的圆盘压在较小的圆盘上面
    那么问题来了,对于一个n阶(阶数即是问题中圆盘的个数)的上述问题,用最少操作次数将圆盘塔从塔座A移动到塔座C的操作总是固定的。请问,n阶问题执行k步操作后,塔座A、B和C上圆盘的情况是怎样的?从大到小输出三个塔座上的圆盘的编号(如果该塔座上没有圆盘,请输出0)。
比如,塔座A上有圆盘1,3;塔座B上有圆盘2;塔座C上有圆盘4。我们将输出:
3 1
2
4
输入描述:
数据有多组,处理到文件结束。
每组数据一行输入,有两个整数n和k,n代表问题的阶数,k代表执行的步数。
输出描述:
每组数据输出占三行。
第一行描述塔座A的情况。
第二行描述塔座B的情况。
第三行描述塔座C的情况。
示例1
输入

3 5
4 10
输出

3 1
2
0
4
3 1
2
备注:
10000组数据。
对于100%的数据,
1 <= n < 40;
1 <= k < (3^n)。


汉诺塔移动是一个递归的过程

比如说 把 n 个 圆盘从第一根柱子移到第三根柱子的过程就是:

1、将第一根柱子上 n-1 个圆盘移到第三根柱子

2、将第一根柱子上的 n 号圆盘移到第二根柱子

3、将第三根柱子上 n-1 个圆盘移回第一根柱子

4、将 n 号圆盘从第二根柱子移到第三根柱子

5、重复步骤 1


可以得到递推式 F(n) = F(n-1) + 1 + F(n-1) + 1 + F(n-1)

根据k可以判断n号圆盘放置的位置,然后不断递推上去,可以得到所有圆盘放置的位置


#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define LL long long
LL n,k,a[50],ans[10][50],sz[10];
void slove(int n,int pos){
    if(n==0) return;
    if(k<=a[n-1]) {
        ans[pos][sz[pos]++] = n;
        slove(n-1, pos);
    }else if(k==a[n-1]+1){
        ans[2][sz[2]++] = n;
        while(--n) ans[4-pos][sz[4-pos]++] = n;
    }else if(k<=2*a[n-1]+1){
        ans[2][sz[2]++] = n;
        k -= a[n-1]+1;
        slove(n-1, 4-pos);
    }else if(k==2*a[n-1]+2){
        ans[4-pos][sz[4-pos]++] = n;
        while(--n) ans[pos][sz[pos]++] = n;
    }else{
        ans[4-pos][sz[4-pos]++] = n;
        k -= 2*a[n-1]+2;
        slove(n-1, pos);
    }
}
int main()
{
    a[0] = 0; a[1] = 2;
    for(int i=2;i<40;i++) a[i] = 3 * a[i-1] + 2;
    while(scanf("%lld%lld",&n,&k)!=EOF){
        memset(sz,0,sizeof sz);
        for(int i=1;i<=n;i++){
            if(a[i]>=k){
                while(n>i) ans[1][sz[1]++] = n--;
                slove(i, 1);
                break;
            }
        }
        for(int i=1;i<=3;i++){
            for(int j=0;j<sz[i];j++){
                printf("%lld%c",ans[i][j],(j==sz[i]-1)?'\n':' ');
            }
            if(sz[i]==0) printf("0\n");
        }
    }
    return 0;
}