天天看點

全排列 回溯法

全排列可以說是最基本的部分了,不過實作的過程還是很有必要學習的,可以說難者不會,會者不難。

大體思路如下:

第一步:從n個數中選取第一個排列的第一個元素,如1;

第一步:從n個數中選取第一個排列的第二個元素,如2;

......

第n步:從n個數中選取第一個排列的第n個元素,如n;

當然不能選重複的。到此,第一個排列已經選出來了。那麼第二個排列怎麼選呢,其實很簡單。

上一個排列執行到第n步後,這個函數不再執行,進行回溯,那麼就會回到第n-1步,這時前面的n-1

個數都已經選過了,是以第n-1步選擇的就會是n了,然後第n步選擇的就是n-1;

是以第一個排列是:1 2 3 。。。n-1 n;

第二個是:1 2 3 。。。n n-1;

以此類推就會輸出所有的排列,代碼如下。

#include<iostream>
using namespace std;
int n,a[100],v[100];
//a數組用于儲存每一次的排列,v數組用于判斷數字是不是已經被選過 
void dfs(int dp)//dp從1到n,執行n次,每次選擇一個數 
{
	if(dp>n)//dp為n+1的時候,就說明已經選了n個,可以輸出了; 
	{
		for(int i=1;i<=n;i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i=1;i<=n;i++) 
	{
		if(!v[i])//判斷是不是選擇過 
		{
			a[dp]=i;//儲存目前選擇 
			v[i]=1;//标記這個數字,防止同一個排列選擇相同的數字。 
			dfs(dp+1);
			v[i]=0;//回溯回來的時候一定要清楚标記,不然下一個排列就能選擇了,這也是最關鍵的地方,仔細思考一下。 
		}
	}
} 
int main()
{
	while(cin>>n)
	{
		dfs(1);
	}
}
           

運作結果:

全排列 回溯法

,很明顯,這是字典序。

還有另外一種實作的方法,有着很好的用處,可以友善的解決一些搜尋的題目。

答題思路就是我選擇了一個元素,那麼就把這個元素交換到目前這個位置,就不用開一個數組标記了,直接給代碼吧,兄弟們

可以自己用筆和紙走一遍,就會明白了,我認為這種思想很有用處的。

#include<iostream>
using namespace std;
int n,a[100];
void dfs(int dp)
{
	if(dp>n)
	{
		for(int i=1;i<=n;i++)
		{
			cout<<a[i]<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i=dp;i<=n;i++)
	{
		swap(a[i],a[dp]);//交換 
		dfs(dp+1);
		swap(a[i],a[dp]);//回溯回來的時候一定要換回來 
	}
}
int main()
{
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)
		{
			a[i]=i;//初始化a數組, 
		}
		dfs(1);
	}
}
           

結果:

全排列 回溯法

很顯然,不是字典序,可以和上一個截圖對比一下,有的題目會要求用字典序輸出,是以要小心一點。

舉一反三的時候到了,兄弟們可以做一下這一道題,完全用的就是上面的方法,如果你能寫出來,那麼就是真的了解了。

題目:

007:素數環

總時間限制: 

1000ms

記憶體限制: 

131072kB

描述

輸入正整數n,把整數1,2,3……n組成一個環,使得相鄰兩個整數之和均為素數。輸出時從整數1開始逆時針排列。同一個環應恰好輸出依次。

輸入

一行,正整數n。

輸出

把這個環從整數1開始逆時針排列。同一個環恰好輸出一次。

每一個數之間用空格隔開。

樣例輸入

6      

樣例輸出

1 4 3 2 5 6
1 6 5 2 3 4      

大家先試一下,想要代碼的可以評論,然後我會把代碼給大家,就按照上面的思想就可以了。

還有,大家覺得講的好的話,可以支援一下作者,當然也可以提建議,,謝謝兄弟們了。

繼續閱讀