再說約瑟夫環

題目來自@陳利人的微博: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都有搭配相似的指令了,速度固然很快。

相關文章
相關標籤/搜索