回溯法基本思想:在包含问题的所有解的解空间树(或森林)中,按照深度优先的策略,从根节点出发搜索解空间树,搜索至解空间树的任一节点时总是要判断该节点是否满足问题的约束条件。如果满足进入该子树继续深度搜索;如果不满足逐层向祖先结点回溯。其实回溯法就是对隐式图的深度优先搜索!
回溯算法的设计主要有下面三种:
一、非递归框架回溯
二、递归框架回溯
三、排列的回溯搜索
设计一些可以先进行排列在进行搜索的问题,采用第三种效率要会低一些
下面是八皇后问题的三种解法:
//排列递归算法,b、c数组记录对角线的占有情况
#include<stdio.h>
#define M 100
int n,p[M],a[2*M],b[2*M],count=0;
void out()
{
int i;
count++;
printf(“%d:”,count);
for(i=1;i<=n;i++)
printf(“%d”,p[i]);
printf(“\n”);
}
void swap(int *x,int *y)
{
int t;
t=*x;
*x=*y;
*y=t;
}
void search(int i)
{
int j;
if(i>n) //因为i=n的时候也要进行一次遍历。小技巧:当运行不出正确结果时,推一遍,看哪里有漏洞
out();
else
for(j=i;j<=n;j++)
{
if(a[i+p[j]]==0&&b[i-p[j]+n]==0)
{
swap(&p[j],&p[i]);
a[i+p[i]]=1;
b[i-p[i]+n]=1;
search(i+1);
a[i+p[i]]=0;//回溯清理
b[i-p[i]+n]=0;
swap(&p[j],&p[i]);
}
}
}
int main()
{
int i;
scanf(“%d”,&n);
for(i=0;i<=n;i++)
{
p[i]=i;
a[i]=0;
a[i+n]=0;
b[i]=0;
b[i+n]=0;
}
search(1);
if(count==0)
printf(“No polution!\n”);
}
//非递归
#include<stdio.h>
#include<math.h>
int n,p[20],count=0;
int check(int k)
{
int i;
for(i=1;i<k;i++)
if(p[k]==p[i]||abs(k-i)==abs(p[k]-p[i]))
return 0;
return 1;
}
void out()
{
int i;
count++;
printf(“%d:”,count);
for(i=1;i<=n;i++)
printf(“%d”,p[i]);
printf(“\n”);
}
void backdate()
{
int k=1;
p[1]=0;
while(k>0)
{
p[k]++;
while(p[k]<=n&&check(k)!=1)
p[k]++;
if(p[k]<=n)
{
if(k==n)
out();
else
{
k++;
p[k]=0;
}
}
else
k–;
}
}
int main()
{
scanf(“%d”,&n);
backdate();
}
//普通递归
#include<stdio.h>
#include<math.h>
int p[20],n,count=0;
void out()
{
int i;
count++;
printf(“%d:”,count);
for(i=1;i<=n;i++)
printf(“%d”,p[i]);
printf(“\n”);
}
int check(int i,int j)
{
int a;
for(a=1;a<i;a++)
if(j==p[a]||abs(i-a)==abs(j-p[a]))
return 0;
return 1;
}
void search(int i)
{
int j;
for(j=1;j<=n;j++)
{
if(check(i,j))
{
p[i]=j;
if(i==n)
out();
else
search(i+1);
}
}
}
int main()
{
scanf(“%d”,&n);
search(1);
return 0;
}