OJ突然打不开了,下次把题目补上。
一开始看到题目,觉得是到简单题,用回溯法就解决了。然后,WA了无数次…
再次认真审题发现,输出结果是
1 2 3 而不是 1 2 3
1 3 2 1 3 2
2 1 3 2 1 3
2 3 1 2 3 1
3 1 2 3 2 1
3 2 1 3 1 2
发现了问题就该改,但是从回溯中没想到改怎么改,在网上搜到了另一种方法——全排列的非递归算法,问题就解决了。
下面贴代码:
回溯法:
<pre name="code" class="cpp">void Perm(int *a,int k,int n)
{
if(k==n-1)
{
for(int i=0;i<n;i++)
{
cout<<a[i];
if(i!=n-1)
{
cout<<" ";
}
}
cout<<endl;
}
else
{
for(int i=k;i<n;i++)
{
int t=a[k];a[k]=a[i];a[i]=t; //改变元素位置
Perm(a,k+1,n);
t=a[k];a[k]=a[i];a[i]=t; //将改变过的位置还原
}
}
}
递归算法通过选择元素,改变位置,达到组成所有排列的结果。
(递归的代码好写一点,也容易理解)
接下来,非递归算法
非递归算法是通过不断找下一个排列,直到找不到下一个排列就停止。(那什么时候找不到下一个排列?比如(3,1,2)的下一个是(3,2,1),而(3,2,1)就是最后一个。
不再有下一个,就这样不好理解,看代码。
(注:找下一个排列的方法,如(3,1,2),先从后向前找到序列中两个相邻并且递增的数,如(1,2),其中小得那个数(1)叫做替换数,它的下标叫做替换点。
然后从替换点向后找一个比替换数大的最小数(一定存在),把这个数和替换数交换,再将替换点后的所有数颠倒,得到的就是下一个排列(字典序)。)
例子:
(2,3,1,4) 先从最后向前找到递增且相邻的两个数1,4;再从1的后面找一个大于1的数,只有4,那么交换,得到(2,3,4,1),然后4以后的数颠倒,还是得到(2,3,4,1)
再找一个;从后向前找到递增且相邻的两个数3,4,;再从3后面找大于3的数,只有4,那么交换,得到(2,4,3,1),然后4以后的数颠倒,得到(2,4,1,3)
代码:
<pre name="code" class="cpp">bool Next_Permutation(int *a,int n)
{
int i,t,k=n-1;
while(k>0)
{
if(a[0]==a[k]) //排除重复元素造成的干扰
{
k--;continue;
}
t=k;
k--;
if(a[k]<a[t]) //找到递增并且相邻的两个数
{
int f=n-1;
for(;f>t;f--) //在替换点以后找一个大于替换数的最小的数
{
if(a[f]<a[t]&&a[f]>a[k])
{break;}
}
int temp=a[f];a[f]=a[k];a[k]=temp; //与替换数交换
int p=t,q=n-1;
while(p<=q) //电脑替换点以后的所有数
{
temp=a[p];a[p]=a[q];a[q]=temp;
}
}
return true;
}
return false; // 这就是最后找不到下一个排列,因为(4,3,2,1)中找不到相邻并且递增的两个数
}