XTU-OJ 1432-Cycle

题目描述

数字 1∼n顺时钟排成一个圈,从1开始顺时钟计数,数到的数退出这个圈,然后从下一个数字开始数,直到剩下最后一个数字。 计数的序列为{a1,a2,…,an−1}, 其中 ai=(ai−1+ai−2)%n+1,i>2 且 1≤a1,a2≤n。

比如n=5,计数序列的前两项为{1,2}时,整个计数序列为{1,2,4,2},那么第一次去掉数字1,得到数字圈为{2,3,4,5}; 再从2开始,数两个数,到数字3,去掉得到数字圈为{4,5,2};依次,再数4个数,到数字4,去掉得到数字圈为{5,2};最后数2个数,去掉2,剩下5。

请你写个程序,在已知n,a1,a2的情况下,求最后剩下的数?

输入

第一行是一个整数T(1≤T≤1000),表示样例的个数。

以后每行一个样例,为三个整数n(2≤n≤1000),a1,a2(1≤a1,a2≤n)。

输出

每行输出一个样例的结果。

样例输入

2
3 2 3
5 2 3

样例输出

1
4

解题思路:本题是把n个数排成一个圆,每次循环去掉一个数。依次循环找到最后剩下的数。第一反应是用链表的方法来解,但大家可能还不太熟悉 指针和结构体,所以这里用了 另一个链表方法:数组链表。(但是还是推荐大家去学一下指针和结构体,就算下面的数组双向链表,也用结构体数组写比较好。)

typedef struct List
{
    int num;      // 数字
    int head;     // 前置节点  题目中的 books[][0];
    int tail;     // 后置节点 == 题目中的 books[][1];
}list[1010];

数组链表: 用二维数组模拟双向链表,books[][0],保存当前数前一个数是多少,books[][1]保存当前数 后一个数是多少 。 比如下图,books[1][0] = 4, books[1][1] = 2 ,表示数字1 前一个数 是4,后一个数是2; books[3][0] = 2, books[3][1] = 4,表示数字3 前一个数 是2,后一个数是4。

现在假如 删除了数字2,那么1后面就接的是3了,3前面一个数就是1了。所以要更新二维数组,books[1][1] = 3, books[3][0] = 1。 这样我们就实现了题目中 去掉对应数字 这一步。

题外话:这是不是很简单就实现了元素删减,但上面为什么还说推荐用结构体数组呢?因为这题,恰好 初始的数字序列和数组下标对的上,是用 1、2、3、4、·····、n-1、n 这么递增上去的,和数组第一维的下标变化一致。但如果现在改变一下题目:圆圈是由各个奇数 1、3、5、······、n-2、n组成的,那么用这种二维数组还方便吗?如果用结构体,直接就可以用 list[i].num 存下每个数值。

AC代码:

方法一:数组链表

#include <stdio.h>

int T,n,end;
int ai[1010],books[1010][3] = {0};
int main()
{
    scanf("%d",&T);
    while ( T --)
    {
        scanf("%d %d %d",&n,&ai[1],&ai[2]);
        books[1][0] = n, books[n][0] = n-1;          // 初始化链表
        books[1][1] = 2, books[n][1] = 1;
        for (int i = 2; i < n; i ++){
            books[i][0] = i-1; books[i][1] = i+1;
        }
        for (int i = 3; i < n; i ++)                 // 把每一轮要走的步数算出来
            ai[i] = (ai[i-2]+ai[i-1])%n + 1;
        end = 1;                                     // 初始在 数字1 位置上
        for (int i = 1; i < n; i ++)                 // 开始删除“圆圈”里的数
        {
            for (int j = 1; j < ai[i]; j ++)         // 走ai[i] 步
                end = books[end][1];                
            books[books[end][0]][1] = books[end][1]; // 更新链表
            books[books[end][1]][0] = books[end][0];
            end = books[end][1];
        }
        printf("%d\n",end);
    }
    return 0;
}

方法二:更新数组法

#include <stdio.h>

int T,n,end,len;
int ai[1010],books[1010] = {0};
int main()
{
    scanf("%d",&T);
    while ( T --)
    {
        scanf("%d %d %d",&n,&ai[1],&ai[2]);
        for (int i = 1; i <= n; i ++)        // 初始化
            books[i] = i;
        
        for (int i = 3; i < n; i ++)
            ai[i] = (ai[i-2]+ai[i-1])%n + 1;
        end = 1, len = n;                       // len表示当前数组长度
        for (int i = 1; i < n; i ++, len --)    // 循环n-1次,相当于走n-1步
        {
            end = (end+ai[i]-1)%len;            // 直接定位到该轮 该删去数 的位置
            if (end == 0)  end = len;
            for (int j = end; j < len; j ++)    // 把删去数后面的所有数往前移(更新数组)
                books[j] = books[j+1];
            if (end == len) end = 1;
        }
        printf("%d\n",books[1]);                // 最后只剩下books[1],就是目标数了
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值