蓝桥杯练习系统 基础练习 2n皇后问题 & 洛谷 P1219 八皇后

试题 基础练习 2n皇后问题

蓝桥杯练习系统 试题 基础练习 2n皇后问题

资源限制

 时间限制:1.0s 内存限制:512.0MB

问题描述

 给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。

输入格式

 输入的第一行为一个整数n,表示棋盘的大小。
 接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。

输出格式

 输出一个整数,表示总共有多少种放法。

样例输入 #1样例输出 #1
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
2
样例输入 #2样例输出 #2
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
0

做这个题之前我们先来看另外一个简单点的题:


P1219 [USACO1.5]八皇后 Checker Challenge

P1219 USACO1.5 八皇后 Checker Challenge - 洛谷

题目描述

 一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

img

 上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6
列号 2 4 6 1 3 5

 这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。并把它们以上面的序列方法输出,解按字典顺序排列。

请输出前 3 个解。最后一行是解的总个数。

输入格式

 一行一个正整数 n,表示棋盘是 n×n 大小的。

输出格式

 前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入 #1输出 #1
62 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

说明/提示

【数据范围】
 对于 100% 的数据,6≤n≤13。

题目翻译来自NOCOW。USACO Training Section 1.5

本题的思路

 经典的深搜题目

 首先:主对角线行-列的差相同,副对角线行+列的和相同。

 那么基本思想就是用三个数组分别记录每一列、左对角线、右对角线上是否有棋子,那么在下次放置之前便可检查某个位置能不能放棋子。再用一个数组记录该行上放置的棋子在哪一列即可。

//c++ --std=c++99
#include <cstdio>

const int MAXV = 120 + 5;

int n, ans;
int line[MAXV];
bool colum[MAXV], ldiag[MAXV * 3], rdiag[MAXV * 3];

void dfs(int);

int main(){
	scanf("%d", &n);
	dfs(1);
	printf("%d", ans);
	return 0;
}

void dfs(int num){
	if (num == n + 1){ //找到一组解
		ans++;
		if (ans <= 3){ //输出前三个结果
			for (int i = 1; i <= n; i++)
				printf("%d ", line[i]);
			printf("\n");
		}
	}
	for (int l = 1; l <= n; l++){
		if (!colum[l]
		 && !ldiag[l - num + 50]
		 && !rdiag[l + num]){ //该位置可以放置
			line[num] = l; //记录该行放置棋子在哪一列
			colum[l] = true,
			ldiag[l - num + 50] = true,
			rdiag[l + num] = true; //标记列与两个对角线
			dfs(num + 1);
			colum[l] = false,
			ldiag[l - num + 50] = false,
			rdiag[l + num] = false; //撤销标记
		}
	}
}
//java
import java.util.Scanner;

public class Main {

	static int MAXV = 120 + 5;

	static int n, ans;
	static int line[];
	static boolean colum[], ldiag[], rdiag[];

	public static void memoryAlloc() {
		line = new int[MAXV];
		colum = new boolean[MAXV];
		ldiag = new boolean[MAXV * 3];
		rdiag = new boolean[MAXV * 3];
	}

	public static void dfs(int num) {
		if (num == n + 1) { // 找到一组解
			ans++;
			if (ans <= 3) { // 输出前三个结果
				for (int i = 1; i <= n; i++)
					System.out.printf("%d ", line[i]);
				System.out.printf("\n");
			}
		}
		for (int l = 1; l <= n; l++) {
			if (!colum[l] && !ldiag[l - num + 50] && !rdiag[l + num]) { // 该位置可以放置
				line[num] = l; // 记录该行放置棋子在哪一列
				colum[l] = true;
				ldiag[l - num + 50] = true;
				rdiag[l + num] = true; // 标记列与两个对角线
				dfs(num + 1);
				colum[l] = false;
				ldiag[l - num + 50] = false;
				rdiag[l + num] = false; // 撤销标记
			}
		}
	}

	public static void main(String[] args) {
		memoryAlloc();
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		dfs(1);
		System.out.println(ans);
	}
}

那么我们现在回到原题。

思路

 在放置每一组皇后时检查该位置是否能放置皇后,每放置一组之后标记该位置已放置。同时仿照上一题方法进行位置标记。

//c++
#include <cstdio>

const int MAXV = 8 + 2;

int n, ans;

int line_1[MAXV];
bool colum_1[MAXV],
	ldiag_1[MAXV * 3],
	rdiag_1[MAXV * 3];

int line_2[MAXV];
bool colum_2[MAXV],
	ldiag_2[MAXV * 3],
	rdiag_2[MAXV * 3];

bool map_p[MAXV][MAXV],
	map_1[MAXV][MAXV];

void dfs_1(int);
void dfs_2(int);

int main(){
	scanf("%d", &n);
	for (int i = 1; i <= n; i++){
		for (int j = 1; j <= n; j++){
			scanf("%d", &map_p[i][j]);
			map_p[i][j] = !map_p[i][j];
		}
	}
	dfs_1(1);
	printf("%d", ans);
	return 0;
}

void dfs_1(int num_1){
	if (num_1 == n + 1){ //找到第一组的一个解
		dfs_2(1);
	}
	for (int l = 1; l <= n; l++){
		if (!colum_1[l]
		 && !ldiag_1[l - num_1 + 10]
		 && !rdiag_1[l + num_1]
		 && !map_p[num_1][l]){ //该位置可以放置
			colum_1[l] = true,
			ldiag_1[l - num_1 + 10] = true,
			rdiag_1[l + num_1] = true; //标记列与两个对角线
			map_1[num_1][l] = true;	   //标记棋盘第一组位置
			dfs_1(num_1 + 1);
			colum_1[l] = false,
			ldiag_1[l - num_1 + 10] = false,
			rdiag_1[l + num_1] = false;
			map_1[num_1][l] = false; //撤销标记
		}
	}
}

void dfs_2(int num_2){
	if (num_2 == n + 1){ //找到第二组的一组解
		ans++;
	}
	for (int l = 1; l <= n; l++){
		if (!colum_2[l]
		 && !ldiag_2[l - num_2 + 10]
		 && !rdiag_2[l + num_2]
		 && !map_p[num_2][l]
		 && !map_1[num_2][l]){ //该位置可以放置
			colum_2[l] = true,
			ldiag_2[l - num_2 + 10] = true,
			rdiag_2[l + num_2] = true; //标记列与两个对角线
			dfs_2(num_2 + 1);
			colum_2[l] = false,
			ldiag_2[l - num_2 + 10] = false,
			rdiag_2[l + num_2] = false; //撤销标记
		}
	}
}

//java
import java.util.Scanner;

public class Main {

	final static int MAXV = 8 + 2;

	static int n, ans;

	static int line_1[];
	static boolean colum_1[], ldiag_1[], rdiag_1[];

	static int line_2[];
	static boolean colum_2[], ldiag_2[], rdiag_2[];

	static boolean map_p[][], map_1[][];

	public static void dfs_1(int num_1) {
		if (num_1 == n + 1) { // 找到第一组的一个解
			dfs_2(1);
		}
		for (int l = 1; l <= n; l++) {
			if (!colum_1[l] && !ldiag_1[l - num_1 + 10] && !rdiag_1[l + num_1] && !map_p[num_1][l]) { // 该位置可以放置
				colum_1[l] = true;
				ldiag_1[l - num_1 + 10] = true;
				rdiag_1[l + num_1] = true; // 标记列与两个对角线
				map_1[num_1][l] = true; // 标记棋盘第一组位置
				dfs_1(num_1 + 1);
				colum_1[l] = false;
				ldiag_1[l - num_1 + 10] = false;
				rdiag_1[l + num_1] = false;
				map_1[num_1][l] = false; // 撤销标记
			}
		}
	}

	public static void dfs_2(int num_2) {
		if (num_2 == n + 1) { // 找到第二组的一组解
			ans++;
		}
		for (int l = 1; l <= n; l++) {
			if (!colum_2[l] && !ldiag_2[l - num_2 + 10] && !rdiag_2[l + num_2] && !map_p[num_2][l]
					&& !map_1[num_2][l]) { // 该位置可以放置
				colum_2[l] = true;
				ldiag_2[l - num_2 + 10] = true;
				rdiag_2[l + num_2] = true; // 标记列与两个对角线
				dfs_2(num_2 + 1);
				colum_2[l] = false;
				ldiag_2[l - num_2 + 10] = false;
				rdiag_2[l + num_2] = false; // 撤销标记
			}
		}
	}

	public static void memoryAlloc() {
		map_p = new boolean[MAXV][MAXV];
		map_1 = new boolean[MAXV][MAXV];
		line_1 = new int[MAXV];
		colum_1 = new boolean[MAXV];
		ldiag_1 = new boolean[MAXV * 3];
		rdiag_1 = new boolean[MAXV * 3];
		line_2 = new int[MAXV];
		colum_2 = new boolean[MAXV];
		ldiag_2 = new boolean[MAXV * 3];
		rdiag_2 = new boolean[MAXV * 3];
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		memoryAlloc();
		n = sc.nextInt();
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				map_p[i][j] = (sc.nextInt() != 1);
			}
		}
		dfs_1(1);
		System.out.println(ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值