显然,直接用STL中的next_permutation(p,p+n)可以很快速得出下一个排列(见算法竞赛入门经典(第二版) P187)
但这道题的重点是理解怎么求下一个排列的方法,
下面是常规方法求解思路
设P是一个排列串:P = s[1]s[2]...s[n] = s[1]...s[j]...s[k]...s[n]
1)从当前排列串的右端开始,找出第一个比右边数字小的数字的序号j
(j从左端开始计算),即 j=max{i|s[i]<s[i+1]};
2)在s[j]的右边的数字中,找出所有比s[j]大的数中最小的数字s[k],
即 k=max{i|s[i]>s[j]}
(对s[j]右边数来说,从右至左是递增的,因此k是所有大于s[j]的数字中序号最大者,
序号虽最大但s[k]是处于s[j]右边且比s[j]大的数中的最小者);
3)交换s[j]和s[k];
4)再将s[j+1]...s[k-1]s[k]s[k+1]...s[n]倒转得到排列
P' = s[1]s[2]...s[j]s[n]...s[k+1]s[k]s[k-1]...s[j+1],
这里P'就是排列P的下一个排列。
简单来说,就是对于给定的一个数,首先从右边找到第一个相邻“有序对”(这个“有序对”
的定义是就是满足s[i]<s[i+1]最右边对)。
现假设下一个要找的数比这个数大,且中间没有一个数比前者大、比后者小。然后再重新从
右边起找出第一个比那个"有序对"的较小者要大的数,交换他们,再将那个较小数下标后面的
子数组反转。
总结一下:就是由后向前找替换数和替换点,然后由后向前找第一个比替换数大的数与替换数
交换,最后颠倒替换点后的所有数据。
刚开始不是很懂,现在大致了解了,但还有些细节不是很能理解,慢慢来吧~~
我的解释:
1.从后向前找相邻的“有序对”,即a[i]<a[i+1],记录p=i;
2.从p之后找比p大中的最小值下标q,
3.交换p,q值
4.把p之后的数倒置
#include <iostream> #include <cstring> #include <cmath> #include <algorithm> #include <cstdlib> #include <cstdio> int n; int a[20]; using namespace std; bool next() { int p; bool change=false; for(int i=n-2; i>=0; i--) //第一步,找p点 { if(a[i]<a[i+1]) { p=i; change=true; break; } } //cout<<"p: "<<p<<endl; if(!change) //如果没有交换,表示已经是最大值了,退出判断 { //cout<<change<<endl; return change; } int cmax=a[p+1],q=p+1; for(int i=p+2; i<n; i++) //第二步,找q点 { if(cmax>a[i]&&a[i]>a[p]) { cmax=a[i]; q=i; } } // cout<<"q: "<<q<<endl; int temp=a[p]; //第三步,交换p,q a[p]=a[q]; a[q]=temp; for(int i=p+1,j=n-1; i<j; i++,j--) //第四步,倒置p点之后的数 { int temp=a[i]; a[i]=a[j]; a[j]=temp; } //cout<<change<<endl; return change; } int main() { char s[20]; int p=1; cin>>n; for(int i=0; i<n; i++) { cin>>s[i]; a[i]=s[i]-'0'; } sort(a,a+n); do { cout<<p<<" "; for(int i=0; i<n; i++) cout<<a[i]; cout<<endl; p++; } while(next()); return 0; }
博客探讨了如何使用常规方法求解字典序的下一个全排列,强调理解next_permutation的实现思路,并引用了算法竞赛入门经典中的相关章节作为参考。

619

被折叠的 条评论
为什么被折叠?



