Josephus again HDOJ3089

约瑟夫循环问题,不过此题的n很大,不能使用O(n)的递推方法,会TLE的

参考了网上的优化方法,是在O(n)的递推方法上优化的

观察递推式 num = (num+m)%i

当num,m比较小,而i比较大时,按照递推式,num的变化实际上是每次增加m的

而增加m的次数其实是可以计算出来的

所以当num+m >= i时我们使用普通递推式, 而num+m < i时我们可以使用下面的方法

设增加次数为x,x要满足下式

num+x*m < i+(x-1) //注意后面是x-1,因为每次都是当num增加完后才增加i的,可以用小数据模拟一下就清楚了

解次式可得

x < (i-num)/(m-1)

即x = floor((i-num)/(m-1))-1

这样我们就可以直接用下面的式子来跳过这些num递增的步骤了

num += x*m

i += x

但要注意此处的 i+x可能会大于n

这时候,其实我们已经可以求得最终解了,只要加个特判就可以,详细可以见注释



//约瑟夫循环问题
//编号从0开始
//假设n = 4, m = 2, k = 1
//第一个出队的人的编号是3
LL josephus(LL n, LL m, LL k)//分别为:人数,出圈步长,起使报数位置
{ 
	LL num = 0;
	if(m == 1)
	{
		return (n-1+k)%n;
	}
	for(LL i = 2; i <= n;)
		if((num+m) < i) 
		{
			LL temp = (i-1-num)%(m-1)? (i-1-num)/(m-1): (i-1-num)/(m-1)-1;
			if(i+temp > n)   //当i+temp > n时特判
			{
				num += (n+1-i)*m;    //直接得到最终解
				break;
			}
			num += temp*m;
			i += temp;
		}
		else
		{
			num = (num+m)%i;  //普通递推
			++i;
		}
	num = (num+k)%n;
	return num;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值