搜索入门练习题3 全组合 题解

题目出处:《信息学奥赛一本通》例5.2。

题目描述

设有 \(n\) 个数的集合 \(\{1,2,...,n\}\) ,从中任意取出 \(r\) 个数进行排列 \((r \le n)\) ,试列出所有的排列。

输入格式

输入包含两个正数 \(n,r(1 \le r \le n \le 10)\)

输出格式

输出从 \(n\) 个数的集合中选出 \(r\) 个数的所有组合,每个组合方案占一行。对于每个组合,按照从小到大的顺序输出组合中的所有元素,两两之间有一个空格分隔。

样例输入

3 2

样例输出

1 2
1 3
2 3

题目分析

“全组合”这道题目和“全排列”很像。首先我们会想到的是使用搜索来解决这个问题。我们这里采用深度优先搜索(DFS)来解决这个问题。
首先我们可以看到这道题是求出 \(n\) 个数中选 \(r\) 个数的所有排列方案,所以我们开一个 ans[] 数组来存放我们当前遍历到的排列的所有方案。ans[id] 用于表示我当前排列中的第 id 个元素的值。然后我们开一个 void f(int id) 函数用于选出当前排列的第 id 个值是什么,选完第 id 个值我们再递归地去选第 id+1 个值,直到到达了边界条件—— id>r ,此时说明我已经找到了排列中的 r 个数了,输出这个方案然后回溯再去遍历看看有没有新的方案。
ans[] 数组的第 id 个位置能放数 i 当且仅当满足如下条件:

  • ans[1]ans[id-1] 中没有一个数等于 i(说明 i 之前没有放过);
  • \(id>1\) 时,要确保 \(ans[id-1] < i\) (要保证排列的前一个元素小于后一个元素)。

实现代码如下:

#include<bits/stdc++.h>
using namespace std;
int n, r, ans[11];
void f(int id) {    // 用于在第id个位置放数
    if (id > r) {   // 边界条件,如果id>r,说明排列的r个数(ans[1]到ans[r])都找到了
        for (int i = 1; i <= r; i ++)
            cout << (i>1 ? " " : "") << ans[i];
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i ++) {
        if (id > 1 && i <= ans[id-1]) continue; // 确保i为我上一个放的数ans[id-1]要大
        bool flag = true;
        for (int j = 1; j < id; j ++)   // 判断是否存在ans[j]==i
            if (ans[j] == i) {
                flag = false;
                break;
            }
        if (flag) {     // 如果flag==true,说明i能放在ans[id]位置
            ans[id] = i;    // 尝试放i,再递归搜索
            f(id+1);
        }
    }
}
int main() {
    cin >> n >> r;
    f(1);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zifeiynoip/p/11450700.html