程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

如何生成升序序列

如何生成升序序列

一个由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;
}

posted on 2013-06-22 11:35  unixfy  阅读(551)  评论(0编辑  收藏  举报

导航