题目描述
数字 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;
}