【模板】回溯法 (八皇后、素数环题解)

回溯法

在问题的解空间树中,按深度优先策略从根结点出发搜索解空间树。算法搜索到解空间树任一结点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯。否则,进入该子树继续按照深度优先策略搜索。

两种剪枝策略

1. 用约束函数在扩展结点处减去不满足约束的子树。

2. 用限界函数剪去得不到最优解的子树。

伪代码模板

void dfs(int t)//t记录递归深度
{
    if(t>n) Output(x);//n控制递归深度
    else
        for(int i=f(n,t);i<=g(n,t);i++){//在当前扩展结点处未搜索过的子树的起始编号和终止编号
            x[t]=h(i);//h(i)表示当前扩展结点x[t]的第i个可选择的值
            if(Constraint(t)&&Bound(t)) dfs(t+1);
        }
}

子集树(如01背包问题)

void dfs(int t)
{
    if(t>n) output(x);
    else{
        for(int i=0;i<=1;i++){
            x[t]=i;
            if(Constraint(t)&&Bound(t)) dfs(t+1);
        }
    }
}

排列树(如TSP问题)

//在dfs(1)以前,需要先将变量数组x[]初始化为单位排列(1,2,3,……)
void dfs(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)) dfs(t+1);
            swap(x[t],x[i]);
        }
    }
}

八皇后

描述

在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。 你的任务是,对于给定的N,求出有多少种合法的放置方法。

输入

共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量

输出

共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。

样例

输入:8

输出:92

输入:5

输出:10

#include<iostream>
#include<cstring>
#include<cmath> 
using namespace std;
int pos[11];
int sum;
int n;
bool place(int k)
{
	for(int i=1;i<k;i++)
	{
		if(pos[i]==pos[k]||abs(i-k)==abs(pos[i]-pos[k])) return false;
	}
	return true;
}
void dfs(int t)
{
	if(t>n) sum++;//深度n+1时,说明到达叶子结点,是一个可选方案 
	else
	{
		for(int i=1;i<=n;i++)//相当于一个n叉树
		{
			pos[t]=i;// 放置棋子 
			if(place(t)) dfs(t+1);//检查落子合法性 
		 } 
	}
}
int main(void)
{
	while(cin>>n)
	{
		memset(pos,0,sizeof(pos));
		sum=0;
		dfs(1);
		cout<<sum<<endl;
	}
	return 0;
}

素数环

描述

素数环是像这样的一个图形,将1~N排列成一个环,让环中任意相邻两个数的和都是素数。如图就是一个简单的素数环:

    1

 /     \

6       4

|        |

5        3

 \      /

     2

注意,考虑到对称性,我们假设素数环的元素都是从1开始的。

输入

第一行为两个正整数 ,代表素数环中元素的个数。0<n<20。

输出

所有可能的素数环,按字典序从小到大输出。

输入

6

输出

1 4 3 2 5 6 
1 6 5 2 3 4

输入

8

输出

1 2 3 8 5 6 7 4 
1 2 5 8 3 4 7 6 
1 4 7 6 5 8 3 2 
1 6 7 4 3 8 5 2

#include<iostream>
#include<cstring>
using namespace std;
int x[21];
bool mark[21];
int n;
bool prime(int num)
{
	int primeNum[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43};
	for(int i=0;i<14;i++){
		if(primeNum[i]==num) return true;
	}
	return false;
}
void output()
{
	for(int i=1;i<=n;i++)
	{
		 if(i==1) cout<<x[i];
		 else printf(" %d",x[i]);
	}
	printf("\n");
}
void dfs(int t)
{
	if(t>n){
		if(prime(x[n]+x[1])==true) output();
		return;
 	}
 	else
 	{
 		for(int i=2;i<=n;i++)
 		{
 			if(mark[i]==true) continue;
 			x[t]=i;
 			mark[i]=true;
 			if(prime(x[t]+x[t-1])) dfs(t+1);
 			mark[i]=false;
		 }
	 }
}
int main(void)
{
	cin>>n; 
	memset(x,0,sizeof(x)); 
	for(int i=1;i<=n;i++){
		mark[i]=false;
	}
	x[1]=1;mark[1]=true;
	dfs(2);
	return 0;
 } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值