约瑟夫环(2012阿里笔试)

N 个数围城一个圈,编号从 1 - N; 给定步长m, 第一个数开始报1,数到 m的数被删除,下个数又从1开始报数,如此循环,直到剩下最后一个数。问最后一个数的编号是?


方法1: 当 m = 2的时候有通项,参见具体数学第一章,J(2^m+p) = 2p + 1 ,其中  0<=p<2^m, m 为N的二进制形式的最高次幂。用数学归纳法可以证明。

提示: J(2n) = 2J(n)-1;  J(2n+1)  = 2J(n) + 1; ,对m 归纳。

当 m > 2的时候不存在通项,只有个递推式。设,J(n)为最后一个数的编号。可知,n个数,第一个删除的数一定是 k = (m-1) % n + 1;  剩下n-1个数:

k+1, k+2, ...... n, 1, 2, ..... k-1  ;下一轮从 k+1 开始报数, 也就是说对应着 1,2,.... , n-1;这样就把原来的 求J(n) 转化为求 J(n-1);假设 J(n-1)=  x ,则把 x 对应回J(n)中的编号y即可, 其中, y = (x -1 + k) % n + 1; 把各个变量代入得到:  J(n) = [J(n-1) - 1 + (m-1)%n+1 ] % n + 1  = [J(n-1) + (m-1)%n ] % n + 1 = ( J(n-1) + m -1 ) % n + 1; 其中 J(1) = 1.于是递归算法可得:

#include <cstdlib>
#include <iostream>

using namespace std;

int josephus(int n,int m)
{
    if(n==1)
        return 1;
    return (josephus(n-1,m)+(m-1))%n+1;
}



int main(int argc, char *argv[])
{
    int n,m;
    while(n)
    {
        cin>>n>>m;
        cout<<josephus(n,m)<<endl;
    }
    system("PAUSE");
    return EXIT_SUCCESS;
}


方法二: 模拟

/**
 *  author:小旦 2012-09-16
 *  暴力法
 */

#include <iostream>
using namespace std;

int K;
const int MAX = 16;
int arr[2 * MAX];
int GoodPNum,BadPNum,res;

void init()
{
	for(int i=1; i<=2*K; i++)  //下标为 1~2K标记为1,代表2K个人
		arr[i] = 1;
	GoodPNum = K;  //被杀之前,好人与坏人都是K个
	BadPNum = K;
}

void solve()
{
	int ModNum = 2 * K + 1;

	res = 2; //返回结果从2开始暴力
	int count,index;   //当count == res的时候,当前下标index的人被杀
	while(true)
	{
		count = 1;
		index = 1;  //遍历数组的下标
		while(true)
		{
			//下标溢出数组上界,则回到初始位置
			if( index == ModNum)
				index = 1;

			//当前所加次数需要杀一个人
			if(count == res)
			{
				count = 0;  //置零,重新开始加

				//开始杀人操作
				arr[index] = -1;
				//如果杀的是坏人
				if(index > K)
					BadPNum--;   //尚未被杀的坏人数减一
				else
					GoodPNum--;  //尚未被杀的好人数减一
				//判断是否找到解
				//坏人杀光了,好人还有剩
				if(BadPNum == 0)
				{
					cout<<res<<endl;
					return;
				}
				//好人杀光了,坏人还有剩
				else if(GoodPNum == 0)
					break; //当前解不是最优解,跳出最里层while
			}
			else {
				//将浮标指到下一个可以杀的人
				while(++index) {
					//下标溢出数组上界,则回到初始位置
					if( index == ModNum)
						index = index % ModNum + 1;
					//找到一个可以杀的人
					if(arr[index] == 1)
						break;
				}
				count++;
			}
		}
		res++; //上一个res值不符合答案,加一继续暴力
	}
}

int main()
{
	while(cin>>K) {
		init();
		solve();
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值