回溯算法

回溯算法有“通用解题”之称。用它可以系统地搜索所有的解。它既可以系统的搜索又可以跳跃式的搜索所有子集。

回溯算法主要有三点必须要彻底的弄清楚

【1】问题解空间的集合,合理的定义解空间集合

【2】确定易于搜索的解空间结构(通常按二叉树的深度遍历,或者图的形式来遍历解空间)

【3】采用深度遍历的方法,在搜索过程中用剪枝函数避免无效搜索

常见的回溯算法:

0-1背包问题

n后问题

旅行者售货员问题(暂时没看,下次再弄)

0-1背包问题  

  例如三种物品的0-1背包问题  1.首先确定其解空间是(0 0 0)(0 0 1)(0 1 0)(0 1 1)(1 0 0)(1 0 1)(1 1 0)(1 1 1)

       通过二叉树的形式将解空间组织起来,采用深度遍历的方法遍历所有的解空间;

       对于w[ 1 2 3 5 ]     p[ 5 8 9 10 ] c=7 的0-1背包问题

void backtrack(int i, int n, int cp, int cw, int bestp, int *w, int *p, int c)//遍历的范围
{ if (i > n)
{ bestp = cp;return ;}
if (cw + w[i] <= c)
{ //进入左子树
cw += w[i]; cp += p[i];
backtrack(i + 1, n, cp, cw, bestp, w, p, c);
cw -= w[i];cp -= p[i];//回溯 }
if (bound(i+1,cw,c,cp,n,w,p) > bestp)//判断是否要剪枝
{ backtrack(i + 1, n, cp, cw, bestp, w, p, c); //进入右子树 }
}

int bound(int i,int cw,int c,int cp,int n,int *w,int *p)
{
int cleft = c - cw;
int bound = cp;
while (i <= n&&w[i] <= cleft)
{
cleft -= w[i];
bound += p[i];
++i;
}
if (i <= n)
{
bound += p[i] * cleft / w[i];
}
return bound;
}

n 皇后问题则更简单一点

  每个皇后用一个数组的保存起来,通过约束条件舍弃分支,回溯到数组的上一个下标,若果可以顺利把所有皇后填入数组中,则该方案可行

皇后是保存在一维数组中,一维数组的下标与一维数组的值可以表示皇后在二维数组中的位置。

bool place(int t,int *x)
{
for (int j = 0; j < t; ++j)
{
if ((abs(t - j) == abs(x[j] - x[t])) || (x[j] == x[t])) return false;
}
return true;
}
void backtrack(int t,int n,int *x)
{
if (t >= n)
{
for (int i = 1; i < 5; ++i)
{
printf("%d", x[i]);
}
printf(" ");
}

for (int i = 1; i <n; ++i)
{
x[t] = i;       //x[1]=1.2.3.4
if (place(t,x)) backtrack(t + 1, n, x);
}
}

主要是解决边界问题以及如何实现边界问题的回溯

原文地址:https://www.cnblogs.com/xcb-1024day/p/11240377.html