再说约瑟夫环

题目来自@陈利人的微博:1到n我的排成一圈,从编号为1的人开始,依次说白,黑,白,黑,。。。,说到黑的人离队,问最后留下编号为几的人。ios

你们都知道,这是很是典型的约瑟夫环问题。其通常形式是M我的,构成一个环(圆),每隔N人就滚蛋,问最后剩下谁。算法

不少同窗说答案是1,这多是由于没理解题目(没上过数据结构?)。好比说三我的,A、B、C,编号分别是一、二、3.数据结构

A说“白”,B说“黑”,C说“白”,而后B滚蛋了,那么剩下A、C。注意,C已经说”白“了,而你们是构成环的,因此A要说“黑”了,因此A滚蛋。最终剩下编号为3的C。spa

经过一个循环链表(队列)能够很方便的模拟。代码我就不写了。模拟,是ACM题目训练里的三大常见题型之一:模拟、数论、DP、贪心、字符串、搜索设计

经过分析(你们能够参考这个连接:http://www.zhihu.com/question/20065611或者参考离散数学教材),能够经过简单的循环左移一位来得到结果,复杂度为O(1)。blog

好比说n=5,那么二进制表示为101,循环左移一位变成011,也就说若是n=5,那么最终惟一剩下来的编号是3(011)队列

可是,这里须要特别注意前导0的存在。好比n=5,那么二进制00000101循环移位后获得00001010结果倒是10,这明显是不对的。ci

 

#include<iostream>
using namespace std;
const int BASE = 32;
int getLeadingZeroCounts(unsigned int x)
{
	int n = 1;
	if (x == 0) return -1;
	if ((x >> 16) == 0) { n = n + 16; x = x << 16; }
	if ((x >> 24) == 0) { n = n + 8; x = x << 8; }
	if ((x >> 28) == 0) { n = n + 4; x = x << 4; }
	if ((x >> 30) == 0) { n = n + 2; x = x << 2; }
	n = n - (x >> 31);
	return n;
}
unsigned int get(unsigned int n)
{
	unsigned int temp1 = (1 << (BASE - getLeadingZeroCounts(n)))-1;
	unsigned int temp2= n << 1;
	unsigned int temp3 = temp1 & temp2;
	return temp3 | 1;
}
int main()
{
	unsigned int n;
	while (cin >> n)
	{
		cout <<get(n) << endl;
	}
}

  

说明:字符串

1,我最先知道循环移位能够解决这个问题,是在高中时候所看的《算法设计与分析基础》(Anany Levitin著)这本书。有兴趣朋友能够参考下。get

2,我最先知道代码中得到前导0的数量的实现,是在本科图书馆里看的《Hacker's Delight》(Henry S. Warren著)这本书。有兴趣朋友能够参考下。

固然,估计不少编译器和CPU都有搭配相似的指令了,速度固然很快。

相关文章
相关标签/搜索