zoukankan      html  css  js  c++  java
  • 如何生成升序序列

    如何生成升序序列

    一个由3个数字组成的序列:ABC,0<=A<=B<=C<=9。问:总共有多少种组合?

             首先,如果不考虑 A<=B<=C,则总共有10*10*10=1000种组合,即000-999。  我们可以根据排列组合的方式来解决这个问题,考虑ABC,3个数字是否相等来讨论,比如3个数字一样时,即如下:

    a)         AAA的形式,总共有10种情况。

    b)        AAB或ABB的形式有:C(10, 2) + C(10, 2) = 45 + 45 = 90种。

    c)         ABC的形式有 C(10, 3) = 10 * 9 * 8 / 6 = 120

    总计有220种组合。

    利用程序实现如下:

    // 3个循环
    #include <iostream>
    using namespace std;
    
    int main()
    {
        int total = 0;
        for (int A = 0; A != 10; ++A)
        {
            for (int B = A; B != 10; ++B)
            {
                for (int C = B; C != 10; ++C)
                {
                    ++total;
                }
            }
        }
        cout << total << endl;
        system("PAUSE");
        return 0;
    }

             针对有3个数字组成的组合,我们的程序可以利用3个循环来得到所有的组合数。但是如果有N个数字组成的组合该如何解决呢?是否要用N个循环呢?显然,使用N个循环只能处理特定的N个数字的情况,不能处理N为变量的情形。

             我们很自然就想到了能不能利用递归方法来实现。

             程序如下:

    // 递归实现
    #include <iostream>
    #include <vector>
    using namespace std;
    
    void foo(int start, int end, int x, int n, int& total)
    {
        if (x > n)
        {
            total = 0;
            return;
        }
        for (int i = start; i <= end; ++i)
        {
            if (x == n)
            {
                ++total;
            }
            else
            {
                foo(i, end, x+1, n, total);
            }
        }
    }
    
    int main()
    {
        int total = 0;
        foo(0, 9, 1, 1, total);
        cout << total << endl;;
        
        total = 0;
        foo(0, 9, 1, 2, total);
        cout << total << endl;;
        
        total = 0;
        foo(0, 9, 1, 3, total);
        cout << total << endl;;
    
        system("PAUSE");
        return 0;
    }

             这个递归程序得到了N个数字的组合数,如果我们想让其输出具体的组合有哪些该怎么办呢?

    1)  尝试I

    // 输出具体的组合
    #include <iostream>
    #include <vector>
    using namespace std;
    
    void foo(int start, int end, int x, int n, int& total)
    {
        if (x > n)
        {
            total = 0;
            return;
        }
        for (int i = start; i <= end; ++i)
        {
            if (x == n)
            {
                cout << i << endl;
                ++total;
            }
            else
            {
                cout << i << ' ';
                foo(i, end, x+1, n, total);
            }
        }
    }
    
    int main()
    {
        int total = 0;
        foo(0, 9, 1, 3, total);
        cout << total << endl;;
    
        system("PAUSE");
        return 0;
    }

             从运行结果可以看到,我们得到是程序运行的过程状态。我们对最后面四行结果进行解释:

             8 8 8

             9

             9 9

             9 9 9

             实际的序列式:888、889、899、999,程序输出的结果并没有把“9”之前的前缀“88”、“99”之前的前缀“8”输出,所以才会产生如上的结果形式。

    整个递归过程是一个树的深度遍历过程,这是一个回溯操作。为了能够得到清晰的结果组合,我们使用一个栈来记录遍历树的过程中的节点,当遍历到最底层时,我们就将该栈收集起来,如果是中间节点,我们先压栈,然后继续遍历,在回溯的时候出栈。

    程序实现如下:

    // 得到组合明细
    #include <iostream>
    #include <vector>
    using namespace std;
    
    void foo(int start, int end, int x, int n, int& total, vector<vector<int> >& vv, vector<int>& v)
    {
        for (int i = start; i <= end; ++i)
        {
            if (x == n)
            {
                v.push_back(i);
                // cout << i << endl;
                ++total;
                vv.push_back(v);
                v.pop_back();
            }
            else
            {
                v.push_back(i);
                // cout << i << ' ';
                foo(i, end, x+1, n, total, vv, v);
                v.pop_back();
            }
        }
    }
    
    int main()
    {
        int total = 0;
        vector<vector<int> > vv;
        vector<int> v;
        foo(0, 9, 1, 3, total, vv, v);
        
        for (vector<vector<int> >::size_type i = 0; i != vv.size(); ++i)
        {
            for (vector<int>::size_type j = 0; j != vv[i].size(); ++j)
            {
                cout << vv[i][j];
            }
            cout << endl;
        }
        
        cout << endl << total << endl;;
        cout << vv.size() << endl;
        cout << v.size() << endl;
    
        system("PAUSE");
        return 0;
    }

    // 如果N=10
    #include <iostream>
    #include <vector>
    using namespace std;
    
    void foo(int start, int end, int x, int n, int& total, vector<vector<int> >& vv, vector<int>& v)
    {
        for (int i = start; i <= end; ++i)
        {
            if (x == n)
            {
                v.push_back(i);
                // cout << i << endl;
                ++total;
                vv.push_back(v);
                v.pop_back();
            }
            else
            {
                v.push_back(i);
                // cout << i << ' ';
                foo(i, end, x+1, n, total, vv, v);
                v.pop_back();
            }
        }
    }
    
    int main()
    {
        int total = 0;
        vector<vector<int> > vv;
        vector<int> v;
        foo(0, 9, 1, 10, total, vv, v);
        
        /*
        for (vector<vector<int> >::size_type i = 0; i != vv.size(); ++i)
        {
            for (vector<int>::size_type j = 0; j != vv[i].size(); ++j)
            {
                cout << vv[i][j];
            }
            cout << endl;
        }
        */
        
        cout << endl << total << endl;;
        cout << vv.size() << endl;
        cout << v.size() << endl;
        
        total = 0;
        vv.clear();
        v.clear();
        foo(0, 9, 1, 11, total, vv, v);
        cout << endl << total << endl;;
        cout << vv.size() << endl;
        cout << v.size() << endl;
    
        system("PAUSE");
        return 0;
    }

    总结:

             递归的实现关键点在于借口的定义:

    void foo (int start, int end, int x, int n, int& total, vector<vector<int> >& vv, vector<int>& v)

             [start, end] 为 第 x 个数字的取值范围;

             x 为第几个数字

             n 为总共几个数字

             total 记录总的组合数

             vv 记录组合明细

             v 记录遍历过程中的组合

             另一方面,是for循环:for (int i = start; i <= end; ++i),一开始就判断x是否等于n,以检测是否到了树叶子节点,或者对于N=1的情况是否有效。如果x不等于n则进入递归调用,由于后面的数字大于等于前面的数字,所以第一个调用参数为i,第3个参数变为x+1。所以,递归函数中的递归调用也是个关键点。

             最后,在实际调用种需要注意的一点是x的取值,根据我们的实现,x的取值应该为1。如果更改递归函数的实现,或许可以设定x的取值为0,这里不再作讨论。

             附:如果不考虑A<=B<=C这个因素,则组合数为1000。程序实现如下:

    // 不考虑A<=B<=C
    #include <iostream>
    #include <vector>
    using namespace std;
    
    void foo(int start, int end, int x, int n, int& total, vector<vector<int> >& vv, vector<int>& v)
    {
        for (int i = start; i <= end; ++i)
        {
            if (x == n)
            {
                v.push_back(i);
                // cout << i << endl;
                ++total;
                vv.push_back(v);
                v.pop_back();
            }
            else
            {
                v.push_back(i);
                // cout << i << ' ';
                foo(start, end, x+1, n, total, vv, v);
                v.pop_back();
            }
        }
    }
    
    int main()
    {
        int total = 0;
        vector<vector<int> > vv;
        vector<int> v;
        foo(0, 9, 1, 3, total, vv, v);
        
        for (vector<vector<int> >::size_type i = 0; i != vv.size(); ++i)
        {
            for (vector<int>::size_type j = 0; j != vv[i].size(); ++j)
            {
                cout << vv[i][j];
            }
            cout << endl;
        }
        
        cout << endl << total << endl;;
        cout << vv.size() << endl;
        cout << v.size() << endl;
    
        system("PAUSE");
        return 0;
    }

  • 相关阅读:
    BZOJ 2119: 股市的预测(后缀数组+rmq)
    2018.12.26 考试(哈希,二分,状压dp)
    ural 1297. Palindrome(后缀数组+rmq)
    URAL 1996. Cipher Message 3(KMP+fft)
    2019.4.11 一题 XSY 1551 ——广义后缀数组(trie上后缀数组)
    bzoj 2553 [BeiJing2011]禁忌——AC自动机+概率DP+矩阵
    洛谷 5291 [十二省联考2019]希望(52分)——思路+树形DP
    LOJ 2587 「APIO2018」铁人两项——圆方树
    洛谷 5289 [十二省联考2019]皮配——分开决策的动态规划
    洛谷 5284 [十二省联考2019]字符串问题——后缀数组+线段树优化连边+真实字典序排序思路
  • 原文地址:https://www.cnblogs.com/unixfy/p/3149494.html
Copyright © 2011-2022 走看看