【POJ】1012 Joseph

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

该问题是Joseph环(约瑟夫环)的变式题,先回顾下约瑟夫环问题。

一、 约瑟夫环问题

1.  问题描述:

已知n个人(以编号123...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。最后一个人为胜利者。

为分析方便n个人编号0 ~ n-1,报数0 ~ m-1

2. 问题分析

i个人,报数0~m-1,编号如下:


第一个出局者易知为(m-1)%i, 记为k-1。最后获胜者记为f(i)。

第一个人出局后有i-1个人,编号如下:


最后获胜者记为f(i-1)。

如果知道f(i-1)便可求的f(i),根据编号对应关系得:f(i) = [f(i-1) +k] %i       (1)

f(1) = 0,结合(1)可求出f(2), f(3), …, f(n)

3. 代码



#include <stdio.h>

int main()
{
	int n, m, i, winner = 0;
	
	scanf("%d%d", &n, &m);
	for (i=2; i<=n; ++i)
	{
		winner = (winner + m) % i;
	}
	
	printf("winner = %d(0~n-1)\n", winner);
	return 1;
}


二、 约瑟夫环变式问题(POJ1012)

1. 问题描述

n个人,2/n是好人,后2/n个人是坏人,寻找最小的m先让2/n个坏人先出去

为分析方便n个人编号0 ~ n-1,报数0 ~ m-1

2. 问题分析

n个人,报数0~m-1,编号如下


第一个出局者易知为 f(1)=(m-1)%n,记为k-1。

第一坏人出局后,剩下i-1个人,编号如下


第二个出局者易知为 (m-1)%(n-1),

从左至右编号,对应为

f(2)=[(m-1)%(n-1) + f(1)] % (n-1)

     =[m-1 + f(1)] % (n-1)


i个出局者从左至右对应编号:f(i) =[(m-1) + f(i-1)] % (n –i + 1),其中f(0)= 0。

该问题求解思路便是枚举m,测试第1个到第2/n个出局者编号 f(i) >= 2/n。


3. m枚举的优化

关于m的枚举,考虑只剩最后一个坏人记为X,需要让该坏人(m-1) % (2/n +1)出局,而报数开始位置需要先找倒数第二个坏人记为B出局的位置,而B的位置只能为以下两种情况:



则可以知道最后一个坏人X出局位置(m-1)%(2/n + 1)= 0或者2/n,可简化为
m% (2/n+ 1) = 0或者1


4. 代码

#include <stdio.h>

int a[14];
int main()
{
	int k, m, i, j, n;
	for (k=1; k<14; ++k)
	{
		if (k == 0)
			break;
			
		n = 2 * k;
		m = k + 1;
		while (m)
		{
			for (i=0, j=0; i<k; ++i)
			{
				j = (m - 1 + j) % (n - i);
				if (j < k)
					break;
			}
			
			if (i == k)
			{
				a[k] = m;
				break;
			}
			
			// m % (k + 1) = 0或者1
			if (m %(k + 1) == 0)
				++m;
			else
				m += k;
		}
	}
	while (scanf("%d", &k))
	{
		if (!k)
			break;
		
		printf("%d\n", a[k]);
	}
	return 0;
}

最后再谈谈关于POJ1012这题的注意事项:

1. 不使用直接暴力计算依次报数,肯定超时;

2. 0<k<14,所以可以使用打表。。。

// 暴力打表
#include <stdio.h>

int a[30];
int main()
{
	int k;
	while (scanf("%d", &k))
	{
		if (k == 0)
			break;
			
		switch(k)
        {
            case 1: printf("2\n");break;
            case 2: printf("7\n");break;
            case 3: printf("5\n");break;
            case 4: printf("30\n");break;
            case 5: printf("169\n");break;
            case 6: printf("441\n");break;
            case 7: printf("1872\n");break;
            case 8: printf("7632\n");break;
            case 9: printf("1740\n");break;
            case 10: printf("93313\n");break;
            case 11: printf("459901\n");break;
            case 12: printf("1358657\n");break;
            case 13: printf("2504881\n");break;
        }
	}
	return 0;
}

3. 就算优化了计算过程,使用递推公式 f(i) = [(m-1) + f(i-1)] % (n – i + 1) 来计算出局人编号,依然存在超时可能,因为其测试数据存在多个重复数据,比如1000个13。。。所以需要先计算1-13对应的值存储起来,再按输入给出输出。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值