递归基础—组合数 / 排列数—输出的各种办法(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\n",b[r]);
34         //cout<<g++<<" "<<it<<"\n";
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<<"\n";
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("\n");
        }
    }
}

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实现(待续)

原文地址:https://www.cnblogs.com/KID-yln/p/12529283.html

时间: 2024-10-11 14:08:46

递归基础—组合数 / 排列数—输出的各种办法(dfs/递归/bfs)的相关文章

Python基础之完数输出

for i in range(2,1000):    s=1    for j in range(2,i):                if i%j==0:            m=i/j            s=s+m    if s==i:         print i,

算法基础:排列组合问题(Golang实现)

[排列组合问题] 一共N辆火车(0<N<10),每辆火车以数字1-9编号,要求以字典序排序输出火车出站的序列号. 输入: 包括N个正整数(0<N<10),范围为1到9,数字之间用空格分割,字符串首位不包含空格. 输出: 输出以字典序排序的火车出站序列号,每个编号以空格隔开,每个输出序列换行. 样例输入: 1 2 3 样例输出: 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 代码实现: package huawei import ( "fmt&qu

如何直观地理解排列数和组合数

如果要问我高中时学文科有什么不好,我觉得,最不好的一点就是在你上概率论课时,你听着老师讲的内容一脸蒙蔽,而其他同学纷纷表示自己高中时就已经学过了.之前做题遇到排列数与组合数都是直接写A和C,并不进行计算,所以对于其公式也只是记住能用就好,但是今天闲着无聊,想试着推导一下排列数和组合数的公式,也为了能深入理解排列数和组合数的原理,所以就开始了天马行空的想象. 对于排列数,可以视为“分步解决”的问题,也就是说: 第一步,从n个某物中选取1个,有n种选择方法: 第二步,从剩下的n-1个某物中选取1个,

输入一个十进制数N,将它转换成R进制数输出(运用递归实现)

#include<stdio.h> int cnt=0;                             //用来记录每个进制存放的位置 char num[20];   //用来存放每个进制的数 void fun(int a,int b) { if(a==0)   //递归的终止条件 return; fun(a/b,b);                       //顺序递归 num[cnt++] = a%b;                  //逆序递归 } int main(

C语言 &#183; 排列数 &#183; 排列式

蓝桥练习场上不断碰到类似的题,都是一个递归搜索的套路. 算法提高 排列数 时间限制:1.0s   内存限制:256.0MB 问题描述 0.1.2三个数字的全排列有六种,按照字母序排列如下: 012.021.102.120.201.210 输入一个数n 求0~9十个数的全排列中的第n个(第1个为0123456789). 输入格式 一行,包含一个整数n 输出格式 一行,包含一组10个数字的全排列 样例输入 1 样例输出 0123456789 数据规模和约定 0 < n <= 10! 作者注释:标准

C语言 &#183; 排列数

算法提高 排列数 时间限制:1.0s   内存限制:256.0MB 问题描述 0.1.2三个数字的全排列有六种,按照字母序排列如下: 012.021.102.120.201.210 输入一个数n 求0~9十个数的全排列中的第n个(第1个为0123456789). 输入格式 一行,包含一个整数n 输出格式 一行,包含一组10个数字的全排列 样例输入 1 样例输出 0123456789 数据规模和约定 0 < n <= 10! 1 #include<stdio.h> 2 #includ

SCU 4424(求子集排列数)

A - A Time Limit:0MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice SCU 4424 Description Time Limit: 1000ms Description Given N distinct elements, how many permutations we can get from all the possible subset of the eleme

hdu 4041 2011北京赛区网络赛F 组合数+斯特林数 ***

插板法基础知识 斯特林数见百科 1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 #include<cstring> 5 #define LL long long 6 #define eps 1e-7 7 #define MOD 1000000007 8 using namespace std; 9 int c[2001][2001]={1},stir2[1005][1005]={1};

将100以内同时能被3和5整除的数输出

#include <stdio.h>void main(){ int m; for(m=1;m<=100;m++) { if(m%3==0&&m%5==0) printf("能被3和5整除的数有:%d\n",m); }} 将100以内同时能被3和5整除的数输出,布布扣,bubuko.com