回溯法

 

回溯法是一种系统的搜索问题解的算法。
回溯法的一般执行步骤如下:
1
、针对问题,定义一个解空间,它包含问题的解。
2
、用适于搜索的方式组织该空间。
3
、以深度优先的方式搜索该空间,并利用限界函数来避免搜索不可能产生解的子空间。
回溯法通常在解空间树上进行搜索,而解空间树通常有子集树和排列树。

1 、当所给的问题是从 n 个元素的集合中找出满足某种性质的子集时,相应的解空间树称为子集树。例如、子集问题和子集和问题。

子集树的回溯框架:

void backtrack(int t)

{

    if (t>=n)  

        output(x);

    else

    {

        x[t] = 0;

        if (constraint0(t) && bound0(t))

            backtrack(t+1);

        x[t] = 1;

        if (constraint1(t) && bound1(t))

            backtrack(t+1);

    }

}

子集问题:求某个集合的所有子集。

#include

using namespace std;

const int N = 4;

int x[N] = {2, 8, 12, 6};

int isUsed[N];

 

void print()

{

    for (int i=0; i

        if (isUsed[i])

            cout<" " ;

    cout<

}

void subSet(int t)

{

    if (t >= N)

        print();

    else

    {

        isUsed[t] = 0;

        subSet(t+1);  // 此问题没有限界函数

        isUsed[t] = 1;

        subSet(t+1);  // 此问题没有限界函数

    }

}

int main()

{

    subSet(0);

    return 0;

}

子集和问题:求某个集合的所有子集,使得该子集满足其和等于给定的某个值。

#include

using namespace std;

const int N = 4;

int x[N] = {2, 8, 12, -6};

int isUsed[N];

int sum = 6;

int temp = 0;

 

void print()

{

    for (int i=0; i

        if (isUsed[i])

            cout<" " ;

    cout<

}

void subSetSum(int t)

{

    if (t >= N)

    {

        if (sum == temp)

            print();

    }

    else

    {

        isUsed[t] = 1;

        if (temp != sum)

        {

            temp += x[t]; // 表示 x[t] 被添加到子集中去

            subSetSum(t+1);

            temp -= x[t]; // x[t] 从子集中取出

        }

 

        isUsed[t] = 0;

        subSetSum(t+1);

    }

}

int main()

{

    subSetSum(0);

    return 0;

}

2 、当所给的问题是从 n 个元素的集合中找出满足某种性质的排列时,相应的解空间树称为排列树。例如、排列问题和 8 皇后问题。

排列树的两种回溯框架:

void backtrack1(int t)

{

    if (t>=n)  

        output(x);

    else

        for (int i=t ; i

        {

              swap(x[t], x[i]);

             if (constraint(t) && bound(t))

                  backtrack(t+1);

           swap(x[t],x[i]);

        }

}

void backtrack2(int t)

{

    if (t>=n)  

        output(x);

    else

        for (int i=0 ; i

        {

            x[t] = i;

            if (constraint(t) && bound(t))

              backtrack(t+1);

        }

}

      排列问题:求某个集合的全排列。

框架 1

#include   

using namespace std; 

const int N = 4;  

int x[N] = {3,5,9,6};  

 

void print()

{  

    for (int i=0; i  

           cout << x[i] << " " ;  

    cout << endl;  

}  

 

void swap(int *x,int i,int j)

{

    int temp;

    temp = x[i];

    x[i] = x[j];

    x[j] = temp;

}

void backtrack(int t)

{

    if (t >= N)

        print();

    for (int i=t; i

    {

          swap(x, t, i);

          backtrack(t+1);

          swap(x, t, i);

    }

}

int main()

{

  backtrack(0);

  return 0;

}

框架 2

#include   

using namespace std; 

const int N = 4;  

int x[N] = {3,5,9,6};  

int t[N]; // 表示位置

void print()

{  

    for (int i=0; i  

           cout << x[t[i]] << " " ;  

    cout << endl;  

}  

bool isEqual(int k)

{

    for (int i=0; i<=k-1; ++i)

        for (int j=i+1;j<=k;++j)

            if (t[i] == t[j])

                return false ;

    return true ;

}

void swap(int *x,int i,int j)

{

    int temp;

    temp = x[i];

    x[i] = x[j];

    x[j] = temp;

}

void backtrack(int i)

{

    if (i >= N)

        print();

    for (int j=0; j

    {

        t[i] = j;

        if (isEqual(i)) // 不能有重复

            backtrack(i+1);

    }

}

int main()

{

  backtrack(0);

  return 0;

}

8 后问题:8X8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

#include

#include

using namespace std;

const int N = 8;

int x[N] = {0,1,2,3,4,5,6,7};

void print()

{

    for (int i=0; i

        cout<" " ;

    cout<

}

bool isPlace(int k)

{

    for (int i=0; i

    {

        if (x[k]==x[i] || abs(x[k]-x[i])==abs(k-i))

            return false ;

    }

    return true ;

}

void swap(int *x,int i,int j)

{

    int temp;

    temp = x[i];

    x[i] = x[j];

    x[j] = temp;

}

void nQueen(int t)

{

    if (t >= N)

        print();

    else

    {

        // 框架 1

        for (int i=t; i

        {

            swap(x, i, t);

            if (isPlace(t))

                nQueen(t+1);

            swap(x, i, t); 

        }

/*      // 框架 2

        for (int i=0; i

        {

            x[t]=i;

            if (isPlace(t))

                nQueen(t+1);

        }

        */

    }

}

int main()

{

    nQueen(0);

    return 0;

}

回溯法是一种比较有效的搜索算法,读者要体会其中的精要,框架只供理解,遇到具体问题一定要具体分析,不要一味的套用框架,这样才能以不变应百变。

推荐网站:好巴鹿(http:// www.haobalu.com )

 

你可能感兴趣的:(算法实践,框架,output,算法)