排列枚举问题

以全排列问题为例,如何枚举出所有排列组合?容易想到的思路就是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
#include 
#include
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);

}

你可能感兴趣的:(算法,算法,dfs,数据结构)