And Then There Was One

题目链接
在这里插入图片描述

约瑟夫环问题

假设有 n n n个数,编号为 0 , 1 , 2 , ⋯ n − 1 0,1,2,\cdots n-1 0,1,2,n1,其中0和 n − 1 n-1 n1相连,形成一个环,如图:

.......
0
1
2
3
n

从0开始数 k k k个,将这个数从环中移除,再数 k k k个,再移除,问最后一个数是多少。
假设 n = 7 , k = 3 n=7,k=3 n=7,k=3。那么第一次删除2,数列变成:

0123456
013456

此时,1和3分离开了,为了使1和3连续,我们可以将列表重新编号,从被删除的下一个数开始编号,即:

\0123456
编号前013456
编号后450123

这样新表的下一个要删除的数编号为2,而旧表中为5。
不难发现,新旧表的下一个待删除的数的编号存在这样一个关系:
D e l e t e d N o d e 新 表 = ( D e l e t e d N o d e 旧 表 − k )   m o d   旧 表 中 剩 余 的 数 的 个 数 DeletedNode_{新表}=(DeletedNode_{旧表}-k)\bmod 旧表中剩余的数的个数 DeletedNode=DeletedNodekmod
根据模数的性质,可以推出:
D e l e t e d N o d e 旧 表 = ( D e l e t e d N o d e 新 表 + k )   m o d   旧 表 中 剩 余 的 数 的 个 数 DeletedNode_{旧表}=(DeletedNode_{新表}+k)\bmod 旧表中剩余的数的个数 DeletedNode=DeletedNode+kmod
那么由6个数删到一个数的数列变化:

n\0123456
7-012 3 \color{red}{3} 3456
6编号前013456
6编号后450123
5编号前45013
5编号后12340
4编号前1340
4编号后3012
3编号前301
3编号后012
2编号前01
2编号后01
1编号前1
1编号后0

S i S_i Si n = i n=i n=i时的编号后的数列, F ( S i ) F(S_i) F(Si)为最后被删除的数在 S i S_i Si中的编号,则:
F ( S i ) = ( F ( S i − 1 ) + k )   m o d   ∣ S i ∣ F(S_i)=(F(S_{i-1})+k)\bmod |S_i| F(Si)=(F(Si1)+k)modSi
并且有: F ( S 1 ) = 0 F(S_1)=0 F(S1)=0
那么有:
F ( S 2 ) = ( F ( S 1 ) + 3 )   m o d   2 = 1 F ( S 3 ) = ( F ( S 2 ) + 3 )   m o d   3 = 1 F ( S 4 ) = ( F ( S 3 ) + 3 )   m o d   4 = 0 F ( S 5 ) = ( F ( S 4 ) + 3 )   m o d   5 = 3 F ( S 6 ) = ( F ( S 5 ) + 3 )   m o d   6 = 0 F ( S 7 ) = ( F ( S 6 ) + 3 )   m o d   7 = 3 F(S_2)=(F(S_1)+3)\bmod 2=1\\ F(S_3)=(F(S_2)+3)\bmod 3=1\\ F(S_4)=(F(S_3)+3)\bmod 4=0\\ F(S_5)=(F(S_4)+3)\bmod 5=3\\ F(S_6)=(F(S_5)+3)\bmod 6=0\\ F(S_7)=(F(S_6)+3)\bmod 7=3\\ F(S2)=(F(S1)+3)mod2=1F(S3)=(F(S2)+3)mod3=1F(S4)=(F(S3)+3)mod4=0F(S5)=(F(S4)+3)mod5=3F(S6)=(F(S5)+3)mod6=0F(S7)=(F(S6)+3)mod7=3
求出了结果为3,与表格中的一致。

回归题目

而题目中是从m开始,而非从第一个开始。因为从第 k − 1 k-1 k1个开始移除,那么接下来会从0开始编号,那么从 m − 1 m-1 m1开始的话接下来会从 m − k m-k mk开始编号,并且题目从1开始编号,因此答案还要加上 m − k + 1 m-k+1 mk+1。有因为 答案 + m − k + 1 +m-k+1 +mk+1有可能小于等于0,因此再判断一下即可。

#include<iostream>
using namespace std;
int main() {
	int n, k, m;
	while (true) {
		cin >> n >> k >> m;
		if (!n && !k && !m) {
			break;
		}
		int ans = 0;
		for (int i = 2; i <= n; ++i) {
			ans = (ans + k) % i;
		}
		ans = (m - k + 1 + ans) % n;
		if (ans <= 0) {
			ans += n;
		}
		cout << ans << endl;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值