1. 程式人生 > >菜鳥系列之C/C++經典試題(五)

菜鳥系列之C/C++經典試題(五)

span data- 復雜 joseph 暫時 color current sep mod

求圓圈中剩下的最後一個數字

題目:n個數字(0,1,…,n-1)形成一個圓圈,從數字0開始。每次從這個圓圈中刪除第m個數字(第一個為當前數字本身。第二個為當前數字的下一個數字)。當一個數字刪除後,從被刪除數字的下一個繼續刪除第m個數字。求出在這個圓圈中剩下的最後一個數字。

本題就是著名的約瑟夫環問題。

本題的解法我們比較easy想到用鏈表,當然我們能夠自己寫一個鏈表。也能夠直接用stl庫中的list實現代碼例如以下:

//使用標準庫
int JosephusProblem_Solution2(int n, int m)
{
	if(n < 1 || m < 1)
		return -1;

	list<int> listInt;
	unsigned i;
	//初始化鏈表
	for(i = 0; i < n; i++)
		listInt.push_back(i);

	list<int>::iterator iterCurrent = listInt.begin();
	while(listInt.size() > 1)
	{
		//前進m - 1步
		for(i = 0; i < m-1; i++)
		{
			if(++iterCurrent == listInt.end())
				iterCurrent = listInt.begin();
		}
		//暫時保存刪除的結點
		list<int>::iterator iterDel = iterCurrent;
		if(++iterCurrent == listInt.end())
			iterCurrent = listInt.begin();
		//刪除結點
		listInt.erase(iterDel);
	}

	return *iterCurrent;
}

分析:上面解法的時間復雜度為(n-1)*m,即(mn)。讓我們試圖來尋找一種更好的解法。

我們用歸納法來分析。假如我們能找到第n-1個數和第n個數關系。我們就能夠一次遞推到刪除到1個數在2個數的位置。3….n個數中的文字,最後得到解。

如今我們來找這個關系:

在這n個數字中,第一個被刪除的數字是(m-1)%n。為簡單起見記為k

那麽刪除k之後的剩下n-1的數字為0,1,…,k-1,k+1,…,n-1,而且下一個開始計數的數字是k+1

相當於在剩下的序列中。k+1排到最前面。從而形成序列k+1,…,n-1,0,…k-1

k+1 -> 0
k+2 -> 1

n-1 -> n-k-2
0 -> n-k-1

k-1 -> n-2

如今我們知道了有n-1個數時last的位置。記為f(n-1,m),那麽怎樣來求得f(n,m)關於f(n-1,m)之間的關系?用X,Y來表示,例如以下:

Y X

k+1 -> 0
k+2 -> 1

n-1 -> n-k-2
0 -> n-k-1

k-1 -> n-2

y=( x+k+1) %n

k = (m-1)%n

所以y=(x+m)%n,終於關系例如以下:

0 n=1
f(n,m)={
[f(n-1,m)+m]%n n>1

所以,我們能夠用上面的是,最後剩下的數即n=1時要刪除的數。 n = 1 時, f(n, m ) = 0由此我們知道, f2 m

代碼例如以下:

int JosephusProblem_Solution4(int n, int m)
{
	if(n < 1 || m < 1)
		return -1;

	vector<int> f(n+1,0);
	for(unsigned i = 2; i <= n; i++)
		f[i] = (f[i-1] + m) % i;

	return f[n];
}

有錯請指出, 分享請標明出處! 謝謝


菜鳥系列之C/C++經典試題(五)