题目
从1~n中,选出m个数字,输出所有可能的选择方案,输出方案每一行数字空格间隔输出,从上到下,字典序小的排在前面。
分析
组合枚举问题也是画一个树,与排列树不同的是,组合树要去除排列树的重复的组合。同时为了保证字典序从小到大的排列,需要人为的进行规定,既相邻的两个数字,后一个数字比前一个数字大。考虑dsf传参的时需要的参数,首先是三个位置的状态way[3],然后是当前枚举到了那一个数字,最后是接下来可以进行选择的数字,(因为人为规定了相邻数字后一个要比前一个大,既传入前一个状态下的数字即可,剩下的候选数字在当前状态数字+1~n之间选择)。
Note
- dfs的优化减支:如果发现分支里面无解的话就提前退出。以本题为例:当前在选第u个数字,如果(u-1)到(n-start+1)(能够选的总个数)<m,既u+m-start<m,一定无解,也就是第1个数字选4,5的分支。
- 数组开辟的时候通常都开辟的大一点避免边界问题的麻烦。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=30;
int way[N];
int m,n;
void dfs(int u,int start) //u当前枚举到第几个数字,start枚举数字从那个数字开始的。
{
//减支
if(u+n-start<m) return;
if(u == m+1) //边界
{
for(int i=1;i<=m;i++) cout<<way[i]<<" ";
puts("");
return;
}
for(int i=start;i<=n;i++)
{
way[u]=i; //第u个数枚举i
dfs(u+1,i+1); //从u+1个数继续继续寻找
way[u] = 0; //恢复现场
}
}
int main()
{
scanf("%d%d",&n,&m);
dfs(1,1); //从第一个个位置开始寻找u=1,第一个开始的数字为1,所以start=1
return 0;
}