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