• 递归基础_组合数_输出的各种办法(递归)_(持续更新)


    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 , 递归函数内部 c=n , it = r  时停止遍历 ,注意第47行的条件;

    组合数比全排列难的地方就在于这个限制 "重复" 条件,首先,对于任何一层的组合,

    1. 遍历首位不会超过n-r+1
    2. 遍历尾部不会超过n,
    3. 遍历总数不会超过r

    因此分为两个部分,dfs函数是对尾部(分支)的遍历 , 利用条件3约束, main函数内是对首位穷举, 方式是循环, 利用条件3约束

    如果用bool 标记已经找过的数,思路和排列差不多,标记每次遍历到直接跳过就行了,返回的时候在函数末尾消除标记就可

    (用全排列改写的组合数输出)

     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 int n;
    11 int r;
    12 int a[11];
    13 bool x[11];
    14 void f(int it,int num) {
    15 //num表示此时已经排列了几层(栈的深度)
    16 //it表示下一个要压入的数
    17     a[num]=it;//入栈
    18     x[it]=1;//mark
    19     if(num==r) {
    20         for(int j=1; j<=r; j++) {
    21             cout<<a[j]<<" ";
    22         }
    23         printf("
    ");
    24     } else {
    25         for(int i=it+1; i<=n; i++) {
    26             if(x[i]==1)continue;
    27             else f(i,num+1);
    28         }
    29     }
    30     x[it]=0;
    31 }
    32 
    33 int main () {
    34     cin>>n>>r;
    35     memset(x,0,sizeof(x));
    36     for(int i=1; i<=n-r+1; i++) {
    37         f(i,1);
    38     }
    39 
    40     return 0;
    41 }
    View Code

    进一步化简,  从num=0,开始记录,

     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 int n;
    11 int r;
    12 int a[11];
    13 bool x[11];
    14 void f(int it,int num) {
    15 //num表示此时已经排列了几层(栈的深度)
    16 //it表示下一个要压入的数
    17     a[num]=it;//入栈
    18     x[it]=1;//mark
    19     if(num==r) {
    20         for(int j=1; j<=r; j++) {
    21             cout<<a[j]<<" ";
    22         }
    23         printf("
    ");
    24     } else {
    25         for(int i=it+1; i<=n; i++) {
    26             if(x[i]==1)continue;
    27             else f(i,num+1);
    28         }
    29     }
    30     x[it]=0;
    31 }
    32 
    33 int main () {
    34     cin>>n>>r;
    35     memset(x,0,sizeof(x));
    36     
    37     f(0,0);
    38     
    39 
    40     return 0;
    41 }
    View Code

    但是如果找规律,就需要明确找的数都只能比上一层的大,然后在此基础上循环到恰好是r个

    以下为— 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 }
    View Code

    从0开始更加简洁;

     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 
    48         dfs(0,0);
    49 
    50     }
    51     puts("");
    52 
    53     return 0;
    54 }
    View Code

    在这基础上,直接把每次得到的序列求和,可以完成这题,

     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 }
    View Code

    当然,求和,其实是不需要求这个序列的,遍历每一种可能,之后不需要把序列存下来,直接检查和,就可以满足了,

    不含媒介数组的办法(注意第 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 }
    View Code

    另外的写法——来源: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;
    }
    View Code

    凡是含—输出该序列—的题目都需要一个 “栈” 存放结果,区别在于,他把上界n-r+1写进了递归函数中,

    表示为 for (i = m; i >= k; i--),思维更复杂,这个每次递归的时候上界都会变,

    另一种写法:也是利用01标记法,但是实现写了一个表 + bfs实现   https://blog.csdn.net/cao2219600/article/details/79587306

    同理也可以使用 01 标记+ dfs实现(待续)

     

     

    老实一点,可爱多了
  • 相关阅读:
    Javascript、CSS和IMG之网页执行探索
    从零开始学习Node.js例子九 设置HTTP头
    从零开始学习Node.js例子八 使用SQLite3和MongoDB
    如何做到 jQuery-free?
    jQuery的deferred对象详解
    使用openxml读取xml数据
    Drupal commerce 性能优化
    DataTable数据进行排序、检索、合并、分页、统计
    jquery实现替代iframe的功能
    9_Jvn框架之实现ORM持久层save操作(第九讲)
  • 原文地址:https://www.cnblogs.com/KID-yln/p/12529283.html
Copyright © 2020-2023  润新知