小球钟(模拟算法)

题目描述

小球钟是一个通过不断在轨道上移动小球来度量时间的设备。 每分钟,一个转动臂将一个小球从小球队列的底部挤走,并将它上升到钟的顶部并将它安置在一个表示分钟,5分钟,15分钟和小时的轨道上。 这样可以显示从1:00到24:59(这正是奇怪之处)范围内的时间,若有3个球在分钟轨道,1个球在5分钟轨道,2个球在15分钟轨道及15个球在小时轨道上,就显示时间15:38。 

当小球通过钟的机械装置被移动后,它们就会改变其初始次序。仔细研究它们次序的改变,可以发现相同的次序会不断出现。由于小球的初始次序最后迟早会被重复,所以这段时间的长短是可以被度量的,这完全取决于所提供的小球的总数。 

 每分钟,最近最少被使用的那个小球从位于球钟底部的小球队列被移走,并将上升安置于显示分钟的轨道上,这里可以放置4个小球。 当第5个小球滚入该轨道,它们的重量使得轨道倾斜,原先在轨道上的4个小球按照与它们原先滚入轨道的次序相反的次序加入到钟底部的小球队列。 引起倾斜的第5个小球滚入显示5分钟的轨道。该轨道可以放置2个球。 当第3个小球滚入该轨道,它们的重量使得轨道倾斜,原先2个小球同样以相反的次序加入钟底部的小球队列。而这第3个小球滚入了显示15分钟的轨道。这里可以放置3个小球。当第4个小球滚入该轨道,它们的重量使得轨道倾斜,原先在轨道上的3个小球按照与它们原先滚入轨道的次序相反的次序加入钟底部的小球队列,而这第4个小球滚入了显示小时的轨道。该轨道同样可以放置23个球,但这里有一个外加的固定的不能被移动的小球,这样小时的值域就变为1到24。 从15分钟轨道滚入的第24个小球将使小时轨道倾斜,这23个球同样以相反的次序加入钟底部的小球队列,然后那第24个小球同样加入钟底部的小球队列。

输入

输入定义了一序列的小球时钟。每个时钟都按照前面描述的那样运作。所有时钟的区别仅在于它们在1:00时钟启动时刻小球初始个数的不同。在输入的每行上给出一个时钟的小球数,它并不包括那个在小时轨道上的固定的小球。合法的数据应在33~250。0表明输入的结束。

输出

输出中每一行只有一个数,表示对应的输入情形中给出的小球数量的时钟在经过多少天的运行可以回到它的初始小球序列。

AC代码:

#include <iostream>
#include <deque>
using namespace std;

const int LINE[4] = {5,3,4,24}; // 每个轨道可以容纳的小球数量

int line[4][26];   //  0  1 2 3  存储 1 ,5 ,15 ,小时的
//  line[i][0] 存储小球的个数  1 - j 存储 哪个球进入了
int solved[300];  // 存储结果 已经计算过了
deque<int> q;  // 队列


int gcd(int m,int n){
    return n == 0 ? m : gcd(n,m % n);
}

int get_day(int n)
{
    int k,j;
    int ans = 1;

    for(int i = 0;i < n;i ++)
    {
        // 计算每一个小球的周期 k

        for(j = q[i],k = 1;j != i;j = q[j],++k);

        ans = ans * k / gcd(ans,k);
    }
    return ans;
}

int work(int n)
{
    q.clear();
    for(int i = 0;i < n;i ++) q.push_back(i);

    while(1)
    {
        line[0][++line[0][0]] = q.front();
        q.pop_front();
        // 枚举 1  5  15 分钟
        for(int i = 0;i < 3;i ++)
            if(line[i][0] == LINE[i])  // 当前这一行已经满了 开始 最后一个球滚到下一行,剩余的球逆序入队
            {
                line[i + 1][++line[i + 1][0]] = line[i][line[i][0]--]; // 最后一个球进入下一层
                // 第i层的球开始逆序入队
                while(line[i][0] != 0){
                    q.push_back(line[i][line[i][0]--]); //  剩余的逆序入队
                }
            }
        //  15分钟的 滚入到小时 看一下小时的有没有满
        if(line[3][0] == LINE[3])  // 24小时了
        {
            int o = line[3][0] --;  // 记录当前这个球的编号
            while(line[3][0] != 0)
                q.push_back(line[3][line[3][0]--]);
            q.push_back(line[3][o]);  // 把最后的球入队
            break;
        }
    }
    return get_day(n);
}

int main()
{
    int n;
    while(cin >> n, n!= 0)
    {
        if(solved[n] != 0) cout << solved[n] << '\n';
        else {
            cout << (solved[n] = work(n)) << '\n';
        }
    }
    return 0;
}

运行结果

完结撒花✿✿ヽ(°▽°)ノ✿ 

我坦白了:"寡人是秦始皇,寡人并没有死,寡人是吃了长生不老药,寡人告诉你们,寡人在陕西有3000吨黄金和300万秦军被封印,现在只需要一个赞就能解封,只要你赞了寡人,待寡人解封之日,寡人就收你。"

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值