题目描述
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
输入格式
两个整数 n,m ,在同一行用空格隔开。
输出格式
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行内的数升序排列,相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 5 7排在1 3 6 8前面)。
数据范围
n>0,
0≤m≤n ,
n+(n−m)≤25
输入样例:
5 3
输出样例:
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
题解:
其实只需要把例1的代码稍加修改就可以了。再增加两个情况,一种是动态数组所选择的数已经超过了m个,或者剩余的数凑不够m个,排除这两种情况就是我们所要的答案了。
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int> num;
void dfs(int k)
{
//如题解所述
if(num.size() > m || num.size() + (n - k + 1) < m)
return;
//到达枚举边界,输出结果并结束
if(k == n + 1)
{
for(int i = 0;i < num.size();++i)
cout << num[i] << " ";
cout << endl;
return;
}
//选择这个数
num.push_back(k);
dfs(k+1);
//回溯
num.pop_back();
//不选择这个数
dfs(k+1);
}
int main(void)
{
cin >> n >> m;
dfs(1);
return 0;
}
第二个dfs(k+1)是什么意思
不选择这个数,进入dfs函数,选择k+1这个数,如此递归循环
第一个就是把k加入到最后的结果集合num里面,第二个就是说不把k加入最后的结果,直接看下一个。
太棒了,我逐渐开始理解这一切
为什么k==n+1的时候输出啊
k==n+1说明所有的数都已经遍历完了,k==n的时候还需要判断是否把k加入当前路径的结果集合,所以k==n+1的时候输出结果就行。
用不用判断m=0的情况?
num.size() + (n - k + 1) < m这句话什么意思不理解
当前组合数长度+剩余数的长度<m
为啥选或不选的顺序调换就是从大到小了呀
需要字典序最小输出,因此选择按照自己的递归搜索树来进行输出,如果调换顺序就先输出第二分支的方案
浅显易懂,Orz
刚接触算法
tql
现在数据貌似改了,需要特判一下m=0的情况,因为在该程序中,如果m=0,会死循环
为社么是k==n+1作为边界输出 如果m等于3 n等于5 假设选前三个 再选不就满足num.size() > m了 不就输出不了数字了
好像懂了 不管怎样k都会到k+1
for(int i = 0;i < num.size();i)为什么改for(int i = 0;num[i];i)就错了,按照一开始5 3的样例,输出numsize为3,结果num[3]也有值
num.size() + (n - k + 1) < m 什么意思
num.size()时当前已经选择的个数,后面的是将后面所有数都选上的个数,如果达不到m个的话,就直接不用接着判断了,return完了
剪枝
num.size():表示当前已经选了的数的个数;
n-k+1:表示剩余的数的个数;
整体表示:即使再选上剩余所有的数也不够m个,就没有必要选了,return
tql
枚举边界为什么不是num.size()==m啊
其中一个是
如果都不选,chosen.size()永远无法达到m,陷入死循环
因为m个位置,dfs(1)表示从第一个位置开始搜的,当dfs(u==m)的时候,说明正在给第m个位置选数,如果边界是num.size() == m的话,则第m个位置并没有选数就回溯了
这个厉害
tql
动态数组的思路真的是非常清晰啊
特别好懂
没看懂那个枚举边界,什么意思
等效于:k > n,即枚举的位置已经越界了,只有n个位置。