1. 程式人生 > >約瑟夫問題(連結串列經典)

約瑟夫問題(連結串列經典)

約瑟夫問題

總時間限制: 1000ms記憶體限制: 65536kB 
描述約瑟夫問題:有n只猴子,按順時針方向圍成一圈選大王(編號從1到n),從第1號開始報數,一直數到m,數到m的猴子退出圈外,剩下的猴子再接著從1開始報數。就這樣,直到圈內只剩下一隻猴子時,這個猴子就是猴王,程式設計求輸入n,m後,輸出最後猴王的編號。

輸入每行是用空格分開的兩個整數,第一個是 n,第二個是 m ( 0 < m,n <=300)。最後一行是:0 0 
輸出對於每行輸入資料(最後一行除外),輸出資料也是一行,即最後猴王的編號樣例輸入
6 2 
12 4 
8 3 
0 0 
樣例輸出


7

解析:這是一個很經典(經典就代表這道題是圍繞著連結串列的核心展開)的連結串列問題,當然也可以不用連結串列來做。

首先要注意幾點:

1.題目的要求是要圍成一個圈,所以要有一點技巧處理:將最後一個的下一個指向第一個

2.每次踢出一個猴砸之後,都是從踢出的那個猴砸的下一個開始,而不是重新從第一個開始。(當時我就是因為這裡理解錯了一直沒做出來,讀題不仔細!)

大致思路:

1.先要定義一個結構體,為其建立一個連結串列。

2.然後是要寫兩個函式:1個是用來表示總猴砸數的改變,一個是用來表示報數猴砸的。

3.主函式就是讀入資料,然後進入函式操作。

下面是程式碼:
#include
using namespace std;
int n,m;
struct node{                   		 //首先定義一個結構體 
	int n;
	node *next,*ahead;        	 //使用兩個指標為結點分配其位置
};
node*ahead;node*tail;
void create(node* &head,int &n){		 //第一個函式是用來表示所剩總猴子數的 
	node* tail=head;						 
	for(int i=1;i<=n;i++){			 //開始從第一個迴圈 
		node* unit=new node;		 //定義一個新的結點 
		unit->n=i;					 //移動結點 
		tail->next=unit;					 
		tail=unit; 
	}
	head=head->next;			//每次都要移動開始的那一個 
	tail->next=new node;		//因為開始的移動了,所以結尾的下一個也要移動到新的第一個去,形成一個環 
	node* unit=head;						
	for(int i=1;i<=n;i++){
		unit->next->ahead=unit;		//相當與插入一個結點 
		unit=unit->next;	//移動這個結點 
}
		tail->next=head;		 //tips:技巧處理:將收尾連線,形成一個環 
		head->ahead=tail;
}
void s(node* &head,int &m){		//第二個函式是用來表示報數的猴子的 
	int x=n-1;int k=0;
	node* unit=head;
	while(x){
		k++;
		if(k==m){
			unit->ahead->next=unit->next;//這裡是刪除一個結點的意思 
			unit->next->ahead=unit->ahead;
			k=0;
			x--;
		}
		unit=unit->next;
	}
	cout<n<>n>>m;				//輸入猴子數n和所數到的那個數將會被退出圈子的數m	
	while(n!=0||m!=0){		//滿足條件就執行上面的函式 
		node*head=new node;
		create(head,n);
		s(head,m);
		cin>>n>>m;
	}
	return 0;
}