next_permutation
排列枚举就是枚举所有元素的排列情况,而对于此类题目大多数都可以用
next_permutation
函数生成各个元素的不同排列解决,因此本文重在学习使用这个函数解决问题。
用法剖析
next_permutation(start,end)
是algorithm
标准库函数,它可以重新排列[start,end)
范围内的数组并产生严格的下一个字典序排列。如果有字典序更高的排列方式,就按这种方式排列并返回为true
,否则就返回false
并重新将排列变成字典序最小的方式。
简单验证一下:
#include<iostream>
#include<algorithm>
using namespace std;
int a[10];
int main()
{
for(int i=1;i<=5;i++)
{
a[i]=i;
}
do
{
for(int i=1;i<=5;i++)
{
cout<<a[i]<<' ';
}
cout<<endl;
}
while(next_permutation(a+1,a+6));
return 0;
}
通过这几行代码我们可以按字典序输出从1到5的全排列(太长了,中间省略QAQ):
1 2 3 4 5
1 2 3 5 4
1 2 4 3 5
1 2 4 5 3
……
1 5 4 3 2
2 1 3 4 5
……
5 4 3 1 2
5 4 3 2 1
可以看到,在输出字典序最大的排列后,程序就停了下来。现在我们修改代码使程序死循环,看看字典序最大时结果会如何变化。
我们将中间几行代码改为:
while(1)
{
next_permutation(a+1,a+6);
for(int i=1;i<=5;i++)
{
cout<<a[i]<<' ';
}
cout<<endl;
}
看一下运行结果:
1 2 3 5 4
1 2 4 3 5
1 2 4 5 3
……
5 4 3 1 2
5 4 3 2 1
1 2 3 4 5
1 2 3 5 4
……
5 4 3 2 1
1 2 3 4 5
……
5 4 3 2 1
1 2 3 4 5
……
5 4 3 2 1
1 2 3 4 5
……
5 4 3 2 1
1 2 3 4 5
……
将两次运行结果对比,我们可以发现当next_permutation函数返回false,也就是字典序达到最大值时,函数就会将其变成字典序最小的排列形式,与前面所说一致。
枚举全排列的时间复杂度是O(n!),一般不能超过11个元素。
实战练习
例题1:全排列问题
这道题是深搜的模板题,如果不知道next_permutation的话,就只能傻傻的用递归写:
#include<iostream>
using namespace std;
const int N=10001;
int path[N],book[N];
int n;
void dfs(int u)
{
if(u==n+1)
{
for(int i=1;i<=n;i++)
printf("%5d",path[i]);
cout<<endl;
return;
}
for(int i=1;i<=n;i++)
{
if(!book[i])
{
path[u]=i;
book[i]=1;
dfs(u+1);
book[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
当初理解不了回溯,被虐的死去活来,而现在借助next_permutation,我们只需要简单的循环就能解决问题:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5;
int a[N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
a[i]=i;
}
do
{
for(int i=1;i<=n;i++)
{
printf("%5d",a[i]);
}
cout<<endl;
}
while(next_permutation(a+1,a+n+1));
return 0;
}
怎么样,感受到它的威力了吗?我们趁热打铁,看一下第二道例题:
例题2:火星人
是不是有点晕?这道题歪歪斜斜的每行都写着”火星人“三个字,我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着两个字是”排列“!(手动滑稽)
用一句话概括题意,其实就是求从1到n的字典序全排列的第m个排列,直接用next_permutation即可:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5;
int a[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
while(m--)
{
next_permutation(a+1,a+n+1);
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<' ';
}
cout<<endl;
return 0;
}