回溯法求解全排列問題(可去除重複排列)
阿新 • • 發佈:2018-12-31
1. 回溯法使用標記法求解
include <cstdio>
include <algorithm>
using namespace std;
int vis[100]; //標記陣列,直接判斷不需要迴圈判斷,迴圈見全排列2.cpp
int re[]= {2,1,2}; //給出資料
int ans[100];//下標記錄
void dfs(int cur,int border)//可以去除重複排列,必須所給集合升序且正整數
{
int i,j,jl;
if(cur==border) //有滿足條件的一組解
{
for(i=0; i<border; i++)
printf ("%d ",re[ans[i]]);
putchar('\n');
}
else
{
jl=0; //記錄上一次這個節點的數值
for(j=0; j<border; j++)
if(!vis[j] && re[j]>jl) //如果本次這個節點的值大於上一次就一定不重複
{
ans[cur]=j;
jl=re[j]; //更新節點數值
vis[j]=1 ; //標記走過的點
dfs(cur+1,border); //滿足條件進入下一層
vis[j]=0; //遞迴返回的時候還原最初狀態,有標記的一步一定要記住還原
}
}
}
int main(void)
{
sort(re,re+3); //必須升序,排序演算法才能保證不重複
dfs(0,3);
return 0;
}
2. 回溯法不使用標記陣列,利用迴圈判斷是否可行
#include <cstdio>
#include <algorithm> //這個全排列不使用標記陣列
using namespace std;
int ans[100];
int re[]= {1,2,1};
void dfs(int cur,int border)
{
int i,j,k,ok,jl;
if(cur>=border)
{
for(k=0; k<border; k++)
printf("%d ",re[ans[k]]);
putchar('\n');
}
else
{
jl=0;
for(j=0; j<border; j++)
{
ok=1; //先認為是有效值下面用迴圈進行檢測是否有效
ans[cur]=j;
for(i=0; i<cur; i++)
if(j==ans[i])
{
ok=0;
break;
}
if(ok && re[j]>jl)
{
jl=re[j];
dfs(cur+1,border);
}
}
}
}
int main(void)
{
sort(re,re+3);
dfs(0,3);
return 0;
}
3. STL庫next_permulation()實現
template<typename T>
bool nextPermulation(T begin, T end) //資料開始地址,結束地址
{
if(begin==end) return false;//空序列
if(end==begin+1) return false;//一個元素
T i=end-1;
while(1)
{
T t=i;
i--;
if(*i < *t)
{
T j=end;
while(!(*i < *--j));
iter_swap(i,j);
reverse(t,end);
return true;
}
if(i==begin)
{
reverse(begin,end); //已經為最大序列 例如:321
return false;
}
}
}
1、從尾部開始往前尋找兩個相鄰的元素第1個元素i,第2個元素j(從前往後數的),且 i < j
2、再從尾往前找第一個大於i的元素k。將i、k**對調**
3、[j,last)範圍的元素置逆(顛倒排列)