[算法]暴力求解法:回溯法
在递归构造中,生成和检查过程可以有机结合起来,从而减少不必要的枚举,这就是回溯法。当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,这种现象称为回溯。正是因为这个原因,递归枚举算法常被称为回溯法,应用十分普遍。
八皇后问题
问题:在棋盘上放置8个皇后,使得它们互不攻击,此时每个皇后的攻击范围为同行同列和同
对角线,要求找出所有解
分析:
- 四皇后问题的解答树
- 既然是逐行放置的,则皇后肯定不会横向攻击,因此只需检查是否纵向和斜向攻击即可。条件“cur-C[cur] == j-C[j] || cur+C[cur] == j+C[j]”用来判断皇后(cur,C[cur])和(j,C[j])是否在同一条对角线上。
代码:
import java.util.Scanner;
public class Main {
static int n;
static int tot=0;
static int[] A=new int[10000];
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
n= s.nextInt();
search(0);
System.out.println(tot);
}
private static void search(int cur) {
if(cur == n) tot++;//当cur等于行数,那么就代表摆放成功
else for(int i=0;i<n;i++)
{
int ok = 1;
A[cur] = i;//尝试把第cur行的皇后放入第i列
for(int j=0;j<cur;j++)
{
if(A[cur]==A[j]||cur-A[cur]==j-A[j]||cur+A[cur]==j+A[j])//判断是否同列同主对角线和同副对角线
{
ok = 0; break;//如果是,则不递归并退循环,回溯
}
}
if(ok>0) search(cur+1);//如果合法,则递归
}
}
}
改进:利用二维数组vis[2][ ]直接判断当前尝试的皇后所在的列和两个对角线是否已有其他皇后。注意到主对角线标识y-x可
能为负,存取时要加上n。
import java.util.Scanner;
public class Main {
static int n;
static int tot=0;
static int[] C=new int[10000]; //用来存储解
static int[][] vis=new int[10000][10000];
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
n= s.nextInt();
search(0);
System.out.println(tot);
}
private static void search(int cur) {
if(cur == n)
{
for(int i=0;i<n;i++)
System.out.print(C[i]);
System.out.println();
tot++;//当cur等于行数,那么就代表摆放成功
}
else for(int i=0;i<n;i++)
{
if(vis[0][i]==0 && vis[1][cur+i]==0 && vis[2][cur-i+n]==0) {
//利用二维数组直接判断
C[cur] = i; //如果不用打印解,整个C数组都可以省略
vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1; //修改全局变量
search(cur+1);
vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0; //切记!一定要改回来
}
}
}
}
素数环(Prime Ring Problem)
输输入正整数n,把整数1, 2, 3,…, n组成一个环,使得相邻两个整数之和均为素数。输出
时从整数1开始逆时针排列。同一个环应恰好输出一次。n≤16。
样例输入:
6
样例输出:
1 4 3 2 5 6
1 6 5 2 3 4
代码:
import java.util.Scanner;
public class Main {
static int n;
static int[] A=new int[1000];
static int[] vis=new int[1000];
static int[] isp=new int[1000];
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
n= s.nextInt();
for(int i = 2; i <= n*2; i++)
isp[i] = is_prime(i);
A[0] = 1;
dfs(1);
}
private static int is_prime(int n)
{
if(n<=1) return 0;
int k = (int) Math.floor(Math.sqrt(n)+0.5);
for(int i=2;i<=k;i++)
if(n%i==0)
return 0;
return 1;
}
private static void dfs(int cur)
{
if(cur==n&&isp[A[0]+A[n-1]]!=0)
{
for(int i=0;i<n;i++)
System.out.print(A[i]+" ");
System.out.println();
}
else for(int i=2;i<=n;i++)
{
if(vis[i]==0&&isp[i+A[cur-1]]!=0)
{
A[cur]=i;
vis[i]=1;
dfs(cur+1);
vis[i]=0;
}
}
}
}