hdu1443(约瑟夫环算法递归应用)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1443
题目大意:
给你k个好人和k个坏人,你给出一个最小的m的值令坏人全部杀死,而好人不能被杀死。然后其中的规则如下的例子来解释:
比如题目说的:k=3,m=5那么会有1,2,3,4,5,6六个人,
m=5的第一遍循环就是,将5号杀死。
然后再从5号开始数下一个第五个的人,将其杀死,即->6->1->2->3->4故,第二次杀的是4号
接着就是->6->1->2->3->6【注意:因为5号已经去世了,所以不可能再遍历到5号】所以这次死亡的是6。
123是好人,456是坏人,456在m=5的情况下全部阵亡,好人都活着。所以这个m是符合题目条件的。
这上面的就是m和k的关系

思路参考来源:
https://blog.csdn.net/liujian20150808/article/details/50932817
思路
首先就是观察一下规律。
先将所有犯人设置为0,1,…,k-1,k,…, 2k-2,2k-1

为什么不是从1开始到2k呢?
如果设置为1到2
k的话,那么第一个处死的人位置p就是m%n(n为这里的犯人总人数也就是2k),那么如果m恰好是等于n的话,也就是最后一个处死,那么这里的值就为0了,那么也就是说当m%n时我们还得重新令p这个值等于n。多加了一步操作。
如果设置为0…到2
k-1的话就可以直接得到p的值,就不用处理最后一个值等于0的情况了

由于题目要求是所有坏蛋总是在好人之前出列,所以我们可以将好人圈定起来,将好人定为非法区域。
所以我们可以设start和end两个变量来记录好人编号的起止,所以一开始的时候可将这两数初始化为:

int start = 0 ; int end = k-1;

我们可以将第一个出去的位置定为kill,第一个循环中的kill = (m-1)%n【n为总人数,2*k】,
从上面的题目大意中的例子大家可以知道,就是下次循环找处死人的位置是以这个为起点的。那么比如紧接着第一个循环之后的就是第二个循环,找的顺序将会为kill+1,kill+2,kill+3…,kill+2*k-2

这个2k-2怎么来的?
就是把这群犯人想象成一个环,第二个又从第1个开始找,1->2->…>2
k-2,
2k-2是2k-1的左边一个,其实就是第二个循环的最后一个了,相当于循环回来了。

然后通过观察我们会发现,每次循环好人的范围都会跟着变,但是嘞,好人的起始点和终点的相对位置是不会变的,所以我们每次的循环只需要更新好人区域的值,判断kill这个位置是否在好人区域中就行了。
然后我们可以看到,第二次查找的循环每个都减去kill+1的值就会变为:
0,1,2,3…,2*k-2又变为了第一次的查找的情况了
那么也就是说,start也符合这种情况,start=start-(kill+1)【是上一次的start减去上一次的kill+1,就得到了这次的start】
代入kill=(m-1)%n,得到start=start-((m-1)%n+1)=start-(m%n-1%n+1)=start-(m%n)
然后防止start为负数,所以,start=(start-m%n+n)%n

为什么start=start-(m%n)可以变为start=(start-m%n+n)%n?
比如证明:(x-a)%n = (x-a+n)%n
右边等于((x-a)%n+n%n)%n = (x-a)%n
等于左边,因此得证。
所以只需要将a看成m%n带入这个证明中就可以了

所以最终start=(start-m%n+n)%n,end=(end-m%n+n)%n

ac代码:

#include <stdio.h>

int judge(int k, int m){
	int start = 0;//记录好人的初始位置 
	int end = k-1;//好人最后一个位置
	int kill;
	for(int i = 2*k;i > k;i--){
		kill = (m-1)%i;//得到处死的位置
		if(kill>=start&&kill<=end){
			return 0;
		}
		start = (start-(m%i)+i)%i;
		end = (end-(m%i)+i)%i;
	}
	return 1;
}


int main(){
	int f[14];
	int k;
	for(int i = 1;i < 14;i++){
		for(int j = 1;;j++){
			if(judge(i,j)==1){
				f[i] = j;
				break;
			}
		}
	}
	while(~scanf("%d",&k)){
		if(k == 0)break;
		printf("%d\n",f[k]);	
	}
	return 0;
	
}

【附录】由于个人觉得参考那位大神的思路的比较多但也算有自己的理解,也不知道是算转载或是原创,暂且定为原创。若引起本人不适,请与我联系,并删除此帖。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值