[PTA]7-28 猴子选大王

Spring-_-Bear 的 CSDN 博客导航

一群猴子要选新猴王。新猴王的选择方法是:让 N 只候选猴子围成一圈,从某位置起顺序编号为 1~N 号。从第 1 号开始报数,每轮从 1 报到 3,凡报到 3 的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?

输入格式:

输入在一行中给一个正整数 N(≤ 1000)。

输出格式:

在一行中输出当选猴王的编号。

输入样例:

11
结尾无空行

输出样例:

7
结尾无空行

来源:

来源:PTA | 程序设计类实验辅助教学平台
链接:https://pintia.cn/problem-sets/14/exam/problems/808

提交:

在这里插入图片描述

题解:

模拟整个选王过程最直观,即构建一个长度为 N 的链表,各节点值为对应的顺序索引;每轮删除第 3 个节点,直至链表长度为 1 时结束,返回最后剩余节点的值即可。模拟法需要循环删除 n - 1 轮,每轮在链表中寻找删除节点需要 m 次访问操作(链表线性遍历),因此总体时间复杂度为 O(nm)。

实际上,本题是著名的 “约瑟夫环” 问题,可使用动态规划解决,将时间复杂度降至 O(n)。

当输入 N = 5 时模拟一下选王过程以理清思路,找出规律(此处假设猴子编号为 0 ~ N-1):

轮数数组开始元素移除元素
1[0, 1, 2, 3, 4]02
2[3, 4, 0, 1]30
3[1, 3, 4]14
4[1, 3]11

经过模拟的四轮删除过程,3 即是最后剩下的数字。接下来从最后剩下的 3 倒着看,我们可以反推出这个数字在之前每个轮次的位置:

  • 第一轮反推,补上 3 个位置,然后模上当时的数组大小 2,位置是 (0 + 3) % 2 = 1;

  • 第二轮反推,补上 3 个位置,然后模上当时的数组大小 3,位置是 (1 + 3) % 3 = 1;

  • 第三轮反推,补上 3 个位置,然后模上当时的数组大小 4,位置是 (1 + 3) % 4 = 0;

  • 第四轮反推,补上 3 个位置,然后模上当时的数组大小 5,位置是 (0 + 3) % 5 = 3;

总结一下反推的过程:数字在之前每个轮次的位置 = (当时 index + 3) % 当时数组大小

题目需要求出最后剩下的数字(选为猴王),则最后剩下元素的数组下标一定为 0,故反推过程下标初始化为 0。又因为最后一轮剩下 2 个元素即当时的数组大小为 2,则从 2 开始反推,直到达到 N 的长度即可反推出最后剩余数字在数组中的下标。需要注意的是模拟删除和反推的过程中猴子的编号是从 0 ~ N-1, 而题目中猴子编号为 1 ~ N,故最后打印结果时需加 1。

#include<stdio.h>

int main(void) {
    int N;
    scanf("%d", &N);

    int index = 0;
    for (int i = 2; i <= N; i++) {
        // 数字在之前每个轮次的位置 = (当前 index + 3) % 上一轮剩余数字的个数
        index = (index + 3) % i;
    }
    printf("%d", index + 1);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春天熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值