以全排列问题为例,如何枚举出所有排列组合?容易想到的思路就是dfs深度优先搜索,比如我要得到长度为m的全排列,那么对于每一层递归,选择未被选择过的数然后再往下继续搜索,直到选择了m个数,此时一种排列组合形成了,想要获得所有的排列组合,就需要进行回溯,将之前标记为已选择的数重新标记为未选择,这样就能得到其他的组合。
for (int i = 1; i <= n; i++){
if(!visited[i - 1]){
a[m] = i; //m为此时递归的层数
visited[i - 1] = 1;
func(m + 1, n, visited); //进行下一层搜索
visited[i - 1] = 0;
}
}
上面的代码是搜索的过程,可以看到我使用了一个标志数组visited, 用于记录数字i是否被取过,只有当数字i未被取过才会继续向下搜索,否则循环取下一个数。
if(m == n){
for (int i = 0; i < n; i++)
printf("%5d", a[i]);
return;
}
当递归层数m(从0开始)与n相等时,输出一种组合,并return进行回溯。
完整代码如下:
void func(int m, int n, bool visited[]){
if(m == n){
for (int i = 0; i < n; i++){
printf("%5d", a[i]);
}
cout << endl;
return;
}
for (int i = 1; i <= n; i++){
if(!visited[i - 1]){
a[m] = i + 1;
visited[i - 1] = 1;
func(m + 1, n, visited);
visited[i - 1] = 0;
}
}
}
//调用方法: func(0, n, visited);
全排列问题的变形:
[NOIP2004 普及组] 火星人
问题描述:
设正整数n为排列的长度,全排列n并按照从小到大的顺序排列,每一种排列组合代表一个数字,例如:
123
132
213
231
312
321
代表的数字
1
2
3
4
5
6
给定一个正整数m,将m加到某个长度为n的排列所代表的数字上,求该数字所对应的排列。
问题分析:这道题和全排列是比较像的,全排列的所有输出结果本身就是从小到大进行排列的,所以我只需要在找到指定排列之后再进行m次回溯。
搜索部分:
for (int i = 1; i <= n; i++){
if(flag == 0){
i = a[k]; //a数组最初保存的是输入的排列
}
if(!visited[i - 1]){
a[k] = i;
visited[i - 1] = 1;
func(k + 1, visited);
visited[i - 1] = 0;
}
}
可以看到我只对代码进行了小部分的修改,flag为0代表还在寻找给定排列的阶段。加flag==0这一个判断主要是为了和后面的回溯进行区分。
if(k == n){
flag++;
if(flag == m + 1){
for (int i = 0; i < n; i++){
cout << a[i];
if(i < n - 1)cout << " ";
}
exit(0);
}
return;
}
当递归层次到达n时,开始进行回溯,寻找+m后的数字所对应的排列,这里的处理是对flag进行累加,当flag从0累加到m+1之后,说明程序找到了给定排列之后的第m个排列,输出然后exit退出程序。
完整代码:
#include<iostream>
#include <cstdio>
#include<math.h>
using namespace std;
int a[10100];
bool visited[10100];
int cnt = 0;
int flag = 0;
int m, n;
void func(int k, bool visited[]){
if(k == n){
flag++;
if(flag == m + 1){
for (int i = 0; i < n; i++){
cout << a[i];
if(i < n - 1)cout << " ";
}
exit(0);
}
return;
}
for (int i = 1; i <= n; i++){
if(flag == 0){
i = a[k];
}
if(!visited[i - 1]){
a[k] = i;
visited[i - 1] = 1;
func(k + 1, visited);
visited[i - 1] = 0;
}
}
}
int main(){
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> a[i];
func(0, visited);
}