POJ 1012&51node 1875 约瑟夫问题

Joseph
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 56028 Accepted: 21418

Description

The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, . . ., n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5 then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved.

Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy.

Input

The input file consists of separate lines containing k. The last line in the input file contains 0. You can suppose that 0 < k < 14.

Output

The output file will consist of separate lines containing m corresponding to k in the input file.

Sample Input

3
4
0

Sample Output

5
30

约瑟夫问题的变种
题意:2k个人,要求经过k轮处决后,正好处决掉后k个人,求最小的处决步数m;

题解:首先,先进行倒推,最后一轮游戏前,剩下的人为GGGG...GGB,那么倒数第二轮的排列为GGGG..GGBX orGGGG..GGXB,X为那一轮要被处决的坏人,所以处决的步数就两种情况,t(k+1) or t(k+1)+1,所以就让步数m从k+1开始以k+1为步幅增加。每次检验m与m+1是否可行。

检验的时候要用到约瑟夫的递推公式。f(i)=(f(i-1)+m-1)%(n-i+1)(f(i)表示第i轮游戏出局的位置),这里是从0开始编号的,而且因为每次前k个人都没被处死,所以前k个位置一直是序号,只要保证每次的出局位置不小于k就可以了。
我的语言表述不清晰,这里举个实例,更直观的表述。

例如:K=4,M=30;

f(0)=0;

f(1)=(f(0)+30-1)%8=5;  序列( 0,1,2,3,4,5,6,7 )中的 5

f(2)=(f(1)+30-1)%7=6;  序列( 0,1,2,3,4,6,7 )中的 7

f(3)=(f(2)+30-1)%6=5;  序列( 0,1,2,3,4,6 )中的 6

f(4)=(f(3)+30-1)%5=4;  序列( 0,1,2,3,4 )中的 4
前面4个的序号一直是完整对应的,后面的序号虽然跟位置不匹配了但是我们并不关心,因为只要每次得出的位置不小于k必然可以将后k个全部出局。这里还有一个小优化就是递推中只用到了f(i)与f(i-1)的递推,所以并不需要开数组来储存,用一个变量f就能进行递推。


#include <iostream>
#include<cstdio>
using namespace std;
int check(int k,int m){
	int fin=0,i;
	for(i=0;i<k;i++){
		fin=(fin+m-1)%(2*k-i);
		if(fin<k)return 0;
	}
	return 1;
}
int main()
{
    int k,m,a[15],n;
    for(k=1;k<=14;k++){
	m=k+1;
	while(1){
		if(check(k,m)){
			a[k]=m;
			break;
		}
		else if(check(k,m+1)){
			a[k]=m+1;
			break;
		}
		m+=k+1;
	}
    }
    while(~scanf("%d",&n)){
		if(!n)break;
		printf("%d\n",a[n]);
	}
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值