题目描述
排列与组合是常用的数学方法,其中组合就是从nnn个元素中抽出rrr个元素(不分顺序且r≤n)r \le n)r≤n),我们可以简单地将nnn个元素理解为自然数1,2,…,n1,2,…,n1,2,…,n,从中任取rrr个数。
输入格式
一行两个自然数n,r(1<n<21,0≤r≤n)。
输出格式
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。
**注意哦!输出时,每个数字需要3个场宽,pascal可以这样:
write(ans:3);
输入输出样例
输入 #1
5 3
输出 #1
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
注意输出格式,从前面开始空格的,组合是无序的
递归+回溯
写递归时:
先看参数返回值,n 总长度,r 需要的长度,start 开始的点(例如第一层为1,第二层就是2)
确定终止条件,当个数满足的时候就停止
单层递归逻辑,用一个数组来保存数据,一层存一个数,然后一直到满足条件后,回溯,即删除上一个数重新换另一条路的新的数
这里的剪枝操作是,单层循环的时候,把n换成 n-(r-path.size())+1,最大可取的数
因为 i 是本次搜索的起始位置,如果n是4,r=3 , 起初我们的 i 至多取到2,然后往下搜索得到结果234;
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
using namespace std;
//vector,在尾部插入和删除
vector<int>path;//符合条件的单一结果
void backtracking(int n,int r,int start)
{
if(path.size()==r)//个数达到条件,终止条件
{
for(int i=0;i<path.size();i++)
{
cout<<setw(3);
cout<<path[i];
}
cout<<endl;
return;
}
for(int i=start;i<=n;i++)//单层搜索//剪枝:n <=> n-(k-path.size())+1
{
path.push_back(i);
backtracking(n,r,i+1);
path.pop_back();//回溯,弹出去一个数,再加新的数
}
}
int main()
{
int n,r;
cin>>n>>r;
backtracking(n,r,1);
return 0;
}