题目出处:《信息学奥赛一本通》例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;
}
原文地址:https://www.cnblogs.com/zifeiynoip/p/11450700.html
时间: 2024-10-10 05:10:03