上一篇文章将“回溯法”中最经典的“八皇后问题”进行了分析,这一篇继续探究回溯法,主要通过紫书上的例题进行研究。
-标准定义主要是:
由n元组(x1,x2,….,xn)组成的一个状态空间E={(x1,x2,…,xn)|xi∈si},给定关于n元组的约束集n,要求E中满足D的全部约束条件的所有n元组。对于约束集D具有完备性的问题P,一旦检测断定某个j元组(x1,x2,…,xj)违反D中的约束,则省略了对部分元素(xj+1,….,xn)的测试。
伪代码:
void search(cur,x[])
if (cur=n) output(x)
else for i=1....k
x[cur] = value[i]
if(constraint(cur)&&bound(cur))//约束条件和边界条件
search(cur+1)
问题描述:输入正整数n,把整数1,2,3…,n组成一个环,使得相邻两个整数之和均为素数。
思路:回溯。先打个素数表,之后根据输入的n,进行回溯
套用模板:
void search(cur,A[],vis[])
if (cur=n) output(A)
else for i=2....n
if(!vis[i]&&isp[i+A[cur-1]])//约束条件和边界条件
A[cur]=i
vis[i]=1
search(cur+1)
vis[i]=0
注意这个vis是辅助的全局变量,作为使用的标记,而在回溯法中如果使用辅助的全局变量,则一定要及时把它们恢复原状。
具体代码如下:
#include
#include
#include
#include
using namespace std;
const int maxn = 10010;
int n;
int A[maxn],isp[maxn],vis[maxn];
int is_prime(int n)
{
if(n<=1) return 0;
int k = floor(sqrt(n)+0.5);
for(int i=2;i<=k;i++)
if(n%i==0)
return 0;
return 1;
}
void dfs(int cur)
{
if(cur==n&&isp[A[0]+A[n-1]])
{
for(int i=0;iprintf("%d ",A[i]);
printf("\n");
}
else for(int i=2;i<=n;i++)
{
if(!vis[i]&&isp[i+A[cur-1]])
{
A[cur]=i;
vis[i]=1;
dfs(cur+1);
vis[i]=0;
}
}
}
int main()
{
int kase = 0;
while(scanf("%d", &n) == 1 && n > 0)
{
if(kase > 0) printf("\n");
printf("Case %d:\n", ++kase);
for(int i = 2; i <= n*2; i++) isp[i] = is_prime(i);
memset(vis, 0, sizeof(vis));
A[0] = 1;
dfs(1);
}
return 0;
}
问题描述:如果一个字符串包含两个相邻的重复子串,则称为“容易的串”,其他的串就为“困难的串”如D、DC、ABDAB等。
输入n和L,由前L个字符组成的,字典序第k小的困难的串。
代码如下:
#include
#include
using namespace std;
const int maxn = 100;
char A[maxn];
int n,L;
int dfs(int cur)
{
if(cur==n){
for(int i=0;iprintf("%c",A[i]+'A');
printf("\n");
return 0;
}
for(int i=0;iint ok = 1;
for(int j=1;j*2<=cur+1;j++)
{
int equal = 1;
for(int k=0;kif(A[cur-k]!=A[cur-k-j])
{
equal = 0;break;//一旦某一对元素不相同那么前一半就不等于后一半元素
}
}
if(equal)
{
ok = 0;//如果相同,则不符合
}
}
if(ok)
{
if(!dfs(cur+1))//如果找到解直接退出
return 0;
}
}
}
int main()
{
while(scanf("%d%d",&n,&L)==2&&n&&L)
{
dfs(0);
}
return 0;
}