回溯法

 

回溯法是一种系统的搜索问题解的算法。
回溯法的一般执行步骤如下:
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 <iostream>

using namespace std;

const int N = 4;

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

int isUsed[N];

 

void print()

{

    for (int i=0; i<N; ++i)

        if (isUsed[i])

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

    cout<<endl;

}

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 <iostream>

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<N; ++i)

        if (isUsed[i])

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

    cout<<endl;

}

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<n; 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<n; i++)

        {

            x[t] = i;

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

              backtrack(t+1);

        }

}

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

框架 1

#include <iostream>   

using namespace std; 

const int N = 4;  

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

 

void print()

{  

    for (int i=0; i<N; ++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<N; ++i)

    {

          swap(x, t, i);

          backtrack(t+1);

          swap(x, t, i);

    }

}

int main()

{

  backtrack(0);

  return 0;

}

框架 2

#include <iostream>   

using namespace std; 

const int N = 4;  

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

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

void print()

{  

    for (int i=0; i<N; ++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<N;++j)

    {

        t[i] = j;

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

            backtrack(i+1);

    }

}

int main()

{

  backtrack(0);

  return 0;

}

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

#include <iostream>

#include <cmath>

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<N; ++i)

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

    cout<<endl;

}

bool isPlace(int k)

{

    for (int i=0; i<k; ++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<N; ++i)

        {

            swap(x, i, t);

            if (isPlace(t))

                nQueen(t+1);

            swap(x, i, t); 

        }

/*      // 框架 2

        for (int i=0; i<N; ++i)

        {

            x[t]=i;

            if (isPlace(t))

                nQueen(t+1);

        }

        */

    }

}

int main()

{

    nQueen(0);

    return 0;

}

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

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

letao

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值