【数学】约瑟夫问题

约瑟夫问题

【题目描述】

       有一圈N个人从0到N-1按顺时针编号,他们从0号开始顺时针从1到K报数,报到K的退出,然后从下一个人开始继续从1报数,直到只剩一人。给定NK,求最后剩的人是几号。

 

【输入格式】

       第一行两个数N,K

 

【输出格式】

       一个数,最后剩的人的编号。

 

【样例】

johnseph.in

johnseph.out

5 3

3

 

【数据规模】

       对于30%的数据,N≤1000;

       对于100%的数据,N≤100000。


这是一个经典经典问题。我用的模拟,链表用dancing links删除操作比较容易。l[r[i]] = l[i]; r[l[i]] = r[i],只有40分。


如果用数学方法的话,递推。

最后一次报数,只剩下1个人,编号为0,出队的是0,。f[1] = 0

这是我们要求出,他在还剩2个人时的编号,是(f[1]+k)%2

当剩下m个人的时候,他的编号是,(f[m-1]+k)%m。

(因为当剩下m-1的时候,我们从0到m-2编号,实际上相当于往后平移了k,要复原我们就加上k再取余)

当剩下n的时候,我们就求出了最后剩下的人在此时的编号是(f[n-1]+k)%n。

输出f[n] + 1


参见百科:


我们知道第一个人(编号一定是m mod n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m mod n的人开始): 
  k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2 
  并且从k开始报0。 
  现在我们把他们的编号做一下转换: 
  k --> 0 
  k+1 --> 1 
  k+2 --> 2 
  ... 
  ... 
  k-2 --> n-2 
  k-1 --> n-1 
  变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k) mod n 
  如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式: 
  令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n] 
  递推公式 
  f[1]=0; 
  f=(f+m) mod i; (i>1) 
  有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1 


#include <cstdio>

long nxt[100010];
long pre[100010];

int main()
{
	freopen("johnseph.in","r",stdin);
	freopen("johnseph.out","w",stdout);

	long n;
	long k;
	scanf("%ld%ld",&n,&k);
	long f = 0;
	for (long i=2;i<n+1;i++)
	{
		f = (f+k)%i;
	}
	printf("%ld",f);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值