2018全国多校算法寒假训练赛(第一周) D

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

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
相信大家都知道汉诺塔问题。那么现在对汉诺塔问题做一些限制,成为一个新的玩法。
在一个底座上,从左到右有三个分别命名为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的情况。

这题目看上去就很麻烦。当然不可能让你模拟。毕竟step很大。3^39了。
所以我们要找规律。找规律嘛,把前面的集中情况的详细步骤列出来。写了下面的程序。

#include<bits/stdc++.h>
#define PI 3.1415926 
#define INF 1e18 
#define inf 1e9
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;

int i;    
void move(int n,char from,char to){  
    printf("第%d步:将%d号盘子%c---->%c\n",i++,n,from,to);  
}  
void Hanio(int n,char start_pos,char tran_pos,char end_pos){  
    if(n==1){    
        move(n,start_pos,tran_pos);  
        move(n,tran_pos,end_pos);
    }  
    else{  
        Hanio(n-1,start_pos,tran_pos,end_pos);   
        move(n,start_pos,tran_pos);                
        Hanio(n-1,end_pos,tran_pos,start_pos);    
        move(n,tran_pos,end_pos);               
        Hanio(n-1,start_pos,tran_pos,end_pos);     
    }  
}  
int main(){  
    int n;  
    while(scanf("%d",&n)==1&&n){  
        i = 1;     
        Hanio(n,'1','2','3');  
        printf("最后总的步数为%d\n",i-1);  
    }  
    return 0;  
}  

就很容易观察到,循环的规律 1->2->3->2->1的规律,然后n号盘子经过3^(n-1)动一次。然后有了这些再用一个优先队列用以输出就方便了。

#include<bits/stdc++.h>
#define PI 3.1415926 
#define INF 1e18 
#define inf 1e9
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;
const int _max = 45;
priority_queue<int> q[4];
ll a[_max];
int n ;
ll k;
int main(){
    a[1] = 1;
    for(int i = 2 ; i < 40 ; i++)   a[i] = a[i-1]*3;
    while(cin>>n>>k){
        for(int i = 1 ; i <= n ; i++){
            ll m = k;
            m /= a[i];
            m %= 6;
            if(m==0 || m==5) q[1].push(i);
            if(m==1 || m==4) q[2].push(i);
            if(m==2 || m==3) q[3].push(i);
        }
        for(int i = 1 ; i <= 3 ; i++){
            int s = q[i].size();
            if(s==0) cout<<s;
            else while(!q[i].empty()){
                if(q[i].size()==1){
                    cout<<q[i].top();
                    q[i].pop();
                    break;
                }
                cout<<q[i].top()<<" ";
                q[i].pop();
            }
            cout<<endl;
        }   
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值