递归基础—组合数 / 排列数—输出的各种办法(dfs/递归/bfs)
Posted kid-yln
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归基础—组合数 / 排列数—输出的各种办法(dfs/递归/bfs)相关的知识,希望对你有一定的参考价值。
B: 部分和问题***(注意部分和 ! = 任意子区间求和不一样)
描述 给你N个数,问你能不能从其中取出一些,让它们的和为K.
输入
第一行包括两个数,N,K,分别代表数字个数,以及和为K. 接下来N行,每行一个数字.
输出
如果能选出一些数和为K, 输出YE5, 否则,输出N0
样例
输入:
4 0
1 -1 2 3
输出:
YE5
输入:
2 2
1 -3
输出:
N0
本题求组合数和,注意pe之外,思维比较基础,办法很多,以下为利用dfs思想实现的一种办法
如果求组合数输出各种可能,直接递归是不行的,那样智能遍历,需要一个媒介数组,存放每种可能,因为要每次返回都要利用上一层的东西
观察 1 2 3 4 对于C(4,3)= C(n,r)
1 2 3
1 2 4
1 3 4
2 3 4
对于(a,b,c)型,最终在 首位 a = n-r+1 = 4-3+1 = 2 时停止遍历 ,注意第47行的条件;
组合数比全排列难的地方就在于这个限制 "重复" 条件,首先,对于任何一层的组合,遍历首位不会超过n-r+1
如果用bool 标记已经找过的数,思路并不难和排列差不多,标记每次遍历到直接跳过就行了,返回的时候在函数末尾消除标记就可
但是如果找规律,就需要明确找的数都只能比上一层的大,然后在此基础上循环到最大值n
以下为— dfs 利用栈—实现的 输出n个数的 全组和 代码:
1 #include <iostream> 2 #include <cstring> 3 #include <queue> 4 #include <cstdio> 5 #include <cmath> 6 #include <map> 7 #include <algorithm> 8 typedef long long ll; 9 using namespace std; 10 const int m=25; 11 ll a[m]; 12 bool t=0; 13 ll sum=0; 14 int n; 15 ll k; 16 //用数组下标输出组合数!!! 17 //C(n,k)=C(n=1,k-1) 18 19 20 //1 2 3 4 5 6 21 //5 22 2 4 6 8 10 22 //5 2 4 6 8 10 23 ll b[m]; 24 //b[m]看作一个栈 25 int g=1; 26 ll r; 27 28 void dfs(int num,int it) { 29 30 b[it]=a[num]; 31 if(it==r) { 32 for(int i=1; i<r; i++)printf("%lld ",b[i]); 33 printf("%lld ",b[r]); 34 //cout<<g++<<" "<<it<<" "; 35 } else { 36 for(int i=num+1; i<=n; i++) { 37 dfs(i,it+1); 38 } 39 } 40 } 41 42 int main() { 43 44 scanf("%d",&n); 45 for(int i=1; i<=n; i++)scanf("%lld",&a[i]); 46 for(r=1; r<=n; r++) { 47 for(int i=1; i<=n-r+1; i++) { 48 sum=0; 49 dfs(i,1); 50 } 51 } 52 puts(""); 53 54 return 0; 55 }
在这基础上,直接把每次得到的序列求和,可以完成这题,
1 #include <iostream> 2 #include <cstring> 3 #include <queue> 4 #include <cstdio> 5 #include <cmath> 6 #include <map> 7 #include <algorithm> 8 typedef long long ll; 9 using namespace std; 10 const int m=25; 11 ll a[m]; 12 bool t=0; 13 ll sum=0; 14 int n; 15 ll k; 16 //用数组下标输出组合数!!! 17 //C(n,k)=C(n=1,k-1) 18 19 20 //1 2 3 4 5 6 21 //5 22 2 4 6 8 10 22 //5 2 4 6 8 10 23 ll b[m]; 24 //b[m]看作一个栈 25 int g=1; 26 ll r; 27 28 29 30 void f(int no,int now) { 31 if(t==1)return; 32 b[now]=a[no];//更新入栈, 33 if(now==r) { //栈满检查 34 sum=0; 35 for(int i=1; i<=r&&t==0; i++) { 36 sum+=b[i]; 37 if(sum==k)t=1; 38 } 39 } else { 40 for(int i=no+1; i<=n; i++) { 41 f(i,now+1); 42 } 43 } 44 } 45 46 int main() { 47 48 scanf("%d%lld",&n,&k); 49 for(int i=1; i<=n; i++)scanf("%lld",&a[i]); 50 for(r=1; r<=n; r++) { 51 for(int i=1; i<=n-r+1; i++) { 52 sum=0; 53 f(i,1); 54 } 55 } 56 if(t==1)cout<<"YE5"; 57 else cout<<"N0"; 58 59 puts(""); 60 61 62 return 0; 63 }
当然,求和,其实是不需要求这个序列的,遍历每一种可能,之后不需要把序列存下来,直接检查和,就可以满足了,
不含媒介数组的办法(注意第 44 行,sum减回来当前值的操作在每次到底之后都要执行)
1 #include <iostream> 2 #include <cstring> 3 #include <queue> 4 #include <cstdio> 5 #include <cmath> 6 #include <map> 7 #include <algorithm> 8 typedef long long ll; 9 using namespace std; 10 const int m=25; 11 ll a[m]; 12 bool t=0; 13 ll sum=0; 14 int n; 15 ll k; 16 //用数组下标输出组合数!!! 17 //C(n,k)=C(n=1,k-1) 18 19 20 //1 2 3 4 5 6 21 //5 22 2 4 6 8 10 22 //5 2 4 6 8 10 23 ll b[m]; 24 //b[m]看作一个栈 25 int g=1; 26 ll r; 27 28 void f2(int no,int now) { 29 sum+=a[no];//更新入栈, 30 //cout<<"sum="<<sum<<" "; 31 32 if(sum==k||t==1) { 33 t=1; 34 return; 35 } 36 if(now<r) { //栈满, 37 //cout<<"sum="<<sum<<" "; 38 // 39 40 for(int i=no+1; i<=n; i++) { 41 f2(i,now+1); 42 } 43 } 44 sum-=a[no];//弹出!!!小心少了这一步 45 } 46 int main() { 47 48 scanf("%d%lld",&n,&k); 49 for(int i=1; i<=n; i++)scanf("%lld",&a[i]); 50 51 for(r=1; r<=n; r++) { 52 for(int i=1; i<=n-r+1; i++) { 53 sum=0; 54 f2(i,1); 55 } 56 } 57 if(t==1)cout<<"YE5"; 58 else cout<<"N0"; 59 puts(""); 60 return 0; 61 }
另外的写法——来源:https://blog.csdn.net/randyjiawenjie/article/details/6784355
问题:求n个数中K个数的组合,假设函数原型为 int combination(int n,int k),其中 n的范围为 1……n,
例如:combination(5,3) 要求输出:543、542、541、531、532、521、432、431、421、321
如果输出时有用到数组,其空间需要在开始动态分配好,结束时释放。还是利用递归,关键:c(m,k) = c(m -1 , k- 1) + c(m - 2, k - 1) + ...+ c(k - 1.k - 1)
# include <stdio.h> # define MAXN 100 int a[MAXN]; /** * 组合问题 *问题描述:找出从自然数1、2、……、m中任取k个数的所有组合。 */ void comb(int m, int k) { int i, j; for (i = m; i >= k; i--) { a[k] = i; if (k > 1) comb(i - 1, k - 1); else { for (j=a[0];j>0;j--) printf("%4d",a[j]); printf(" "); } } } int main() { a[0] = 3; // int a[] = {1,2,3,4,5}; comb(5, 3); return 0; }
凡是含—输出该序列—的题目都需要一个 “栈” 存放结果,区别在于,他把上界n-r+1写进了递归函数中,
表示为 for (i = m; i >= k; i--),思维更复杂,这个每次递归的时候上界都会变,
另一种写法:也是利用01标记法,但是实现写了一个表 + bfs实现 https://blog.csdn.net/cao2219600/article/details/79587306
同理也可以使用 01 标记+ dfs实现(待续)
以上是关于递归基础—组合数 / 排列数—输出的各种办法(dfs/递归/bfs)的主要内容,如果未能解决你的问题,请参考以下文章