[笔记]回溯法

  回溯法(back trace method)是一种有组织的系统化搜索技术,可以看作是蛮力法穷举搜索的改进。蛮力法穷举搜索首先生成问题的可能解,然后再去评估可能解是否满足约束条件。而回溯法每次只构造可能解的一部分,然后评估这个部分解,如果这个部分解有可能导致一个完整解,对其进一步构造,否则,就不必继续构造这个部分解了。回溯法常常可以避免搜索所有的可能解,所以,它适用于求解组合数量较大的问题。

概述

解空间树的动态搜索

  蛮力法是对整个解空间树中的所有可能解进行穷举搜索的一种方法,但是,只有满足约束条件的解才是可行解,只有满足目标函数的解才是最优解,这就有可能减少搜索空间。回溯法从根节点出发,按照深度优先策略遍历解空间树,搜索满足约束条件的解。在搜索至树中任一结点时,先判断该结点是否包含问题的(最优)解,如果肯定不包含,则跳过对以该节点为根的子树的搜索,即所谓剪枝(pruning);否则,进入以该节点为根的子树,继续按照深度优先策略搜索。

回溯法的求解过程

  由于问题的解向量$X=(x_1,x_2,...,x_n)$中每个向量$x_i(1 \leq i \leq n)$都属于一个有限集合$Si={a{i1},a{i2}...,a{ir_i}}$,因此,回溯法可以按某种顺序(如字典序)依次考察笛卡尔积$S_1 \times S_2 \times ... \times S_n$中的元素。初始时,令解向量X为空,然后,从根节点出发,选择$S_1$的第一个元素作为解向量X的的第一个分量,即$x1 = a{11}$,如果$X=(x_1)$是问题的部分解,则继续扩展解向量X,选择$S_2$的第一个元素作为解向量X的第二个分量,否则,选择$S_1$的下一个元素作为解向量X的第一个分量,即$x1 = a{12}$。以此类推,一般情况下,如果$X=(x_1,x_2,...,xi)$是问题的部分解,则选择$S{i+1}$的第一个元素作为解向量X的第i+1个分量时,有下面三种情况:
  (1)如果$X=(x_1,x2,...,x{i+1})$是问题的最终解,则输出这个解。如果问题只希望得到一个解,就结束搜索,否则继续搜索其他解。
  (2)如果$X=(x_1,x2,...,x{i+1})$是问题的部分解,则继续构造解向量的下一个分量。
  (3)如果$X=(x_1,x2,...,x{i+1})$既不是问题的部分解,也不是问题的最终解,则存在下面两种情况:

  • 如果$x{i+1} = a{i+1k}$不是集合$S{i+1}$的最后一个元素,则令$x{i+1} = a_{i+1 k+1}$,即选择$S_i+1$的下一个元素作为解向量X的第i+1个分量;

  • 如果$x{i+1} = a{i+1k}$是集合$S_{i+1}$的最后一个元素,就回溯到$X=(x_1,x_2,...,x_i)$,选择$S_i$的下一个元素作为解向量X的第一个分量,假设$xi = a{ik}$,如果$a_{ik}$不是集合$S_i$的最后一个元素,则令$xi = a{ik+1}$;否则,就继续回溯到$X=(x_1,x2,...,x{i-1})$。

    回溯算法的一般框架:

主算法
X = {}
flag = false;
advance(1);
if(flag)
    输出解X
else 
    输出无解

advance(int k)
{
    对每一个x在Sk循环执行下列操作
        xk = x
        将xk加入X
        if(X是最终解) flag = true; return;
        else if(X是部分解)
            advance(k+1)
}

  回溯法的非递归迭代形式的一般框架如下:

X = {}
flag = flase
k = 1
while(k >=1)
    当(Sk没有被穷举)循环执行下列操作
        xk=Sk中的下一个元素
        将xk加入X
        if(X为最终解) 
            flag = true
            break
        else(X为部分解)
            k = k+1
            continue
    k = k - 1

if flag输出解X
else
    输出无解

典型问题

八皇后问题

  八皇后问题是19世纪著名数学家高斯于1850年提出。问题是:在8*8的棋盘上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或统一斜线上。可以把八皇后问题扩展到n皇后问题,即在n*n的的期盼上摆放n个皇后,使其任意两个皇后都不能处于同一行、同一列或同一斜线上。
  显然,棋盘的每一行上可以而且必须摆放一个皇后,所以,n皇后问题的可能解用一个n元向量$X=(x_1,x_2, ...x_n)$表示,其中,$1 \leq i \leq n$,并且$1 \leq x_i \leq n$, 即第i个皇后放在第i行第$x_i$列上。
  由于两个皇后不能位于同一列上,所以解向量X必须满足约束条件:
  

x_i \neq x_j

  若两个皇后摆放的位置分别为$(i,x_i)$和$(j,x_j)$,在棋盘上斜率为-1的斜线上,满足条件$i-j = x_i - x_j$,在棋盘上斜率为1的斜线上,满足条件$i+j = x_i + x_j$,综合两种情况,由于两个皇后不能位于同一个斜线上,所以,解向量X必须满足约束条件:

|i - x_i| \neq | j - x_j|

  八皇后问题的参考代码:

#include <stdio.h>
#include <math.h>

int x[20] = {0};

//考察皇后k放置在x[k]列是否发生冲突
int Place(int k)
{
    int i = 0;

    for(i = 0; i < k; i++)
    {
        if(x[k] == x[i] || (abs(k-i) == abs(x[k] - x[i])))
            return 0;
    }

    return 1;
}

void Queue(int n)
{
    int i = 0;
    int j = 0;
    int k = 0;

    //初始化
    for(i = 1; i <= n; i++)
        x[i] = 0;

    k = 1;
    while(k >= 1)
    {
        x[k] = x[k] + 1; //在下一列放置第k个皇后
        while(x[k] <= n && !Place(k))
            x[k] = x[k] + 1; //搜索下一列

        if(x[k] <= n && k == n) //得到一个解,输出
        {
            for(i = 1; i <= n; i++)
            {
                for(j = 1; j <= n; j++)
                {
                    if(j == x[i])
                        printf(" Q ");
                    else
                        printf(" x ");
                }
                printf("\r\n");
            }

            return;
        }
        else if(x[k] <= n && k < n)
        {
            //放置下一个皇后
            k = k + 1;
        }
        else
        {
            x[k]= 0; //重置x[k], 回溯
            k = k - 1;
        }
    }
}

int main(int argc, const char * argv[]) {
    Queue(4);
    return 0;
}

  输出为:

 x  Q  x  x 
 x  x  x  Q 
 Q  x  x  x 
 x  x  Q  x 

找了个比较好的博文可以参考下:
八皇后问题

回溯法解全排列问题

#include <stdio.h>

int array[10] = {0};
int count = 0;
void permutation(int *array, int begin_index, int length)
{
    int i = 0;
    int tmp;

    if(begin_index == length)
    {
        for(i = 0; i < length; i++)
        {
            printf("%d\t", array[i]);
        }
        printf("\r\n");
        count++;
    }
    else
    {
        for(i = begin_index; i < length; i++)
        {

            tmp = array[begin_index];
            array[begin_index] = array[i];
            array[i] = tmp;
            permutation(array, begin_index+1, length);
            tmp = array[begin_index];
            array[begin_index] = array[i];
            array[i] = tmp;
        }
    }
}

int main(int argc, const char * argv[]) {

    int i = 0;
    int n = 10;

    for(i = 0; i < n; i++)
    {
        array[i] = i;
    }

    permutation(array, 0, n);
    printf("\r\ncount of array:%d\r\n", count);

    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇