全排列可以說是最基本的部分了,不過實作的過程還是很有必要學習的,可以說難者不會,會者不難。
大體思路如下:
第一步:從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
大家先試一下,想要代碼的可以評論,然後我會把代碼給大家,就按照上面的思想就可以了。
還有,大家覺得講的好的話,可以支援一下作者,當然也可以提建議,,謝謝兄弟們了。