【回溯 Kuang】C031_棋盘问题(暴搜)

一、题目描述

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。

要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放 k 个棋子的所有可行的摆放方案数目 C。

输入格式

  • 输入含有多组测试数据。
  • 每组数据的第一行是两个正整数 n,k,用一个空格隔开,表示了将在一个 n∗n 的矩阵内描述棋盘,以及摆放棋子的数目。当为-1 -1时表示输入结束。
  • 随后的 n 行描述了棋盘的形状:每行有 n 个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

输出格式

  • 对于每一组数据,给出一行输出,输出摆放的方案数目 C (数据保证 C<231)。

数据范围

  • n≤8,k≤n
输入样例:
2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1
输出样例:
2
1

二、题解

方法一:回溯

  • 从某一点,例如左上角 ( 0 , 0 ) (0, 0) (0,0) 开始,不断尝试该行中的每一列的合法位置。
    • 合法位置是指:为 # 和 没有被搜索过的行。
  • ( i , j ) (i, j) (i,j) 被搜索过,自然标记为 visited,注:某一行被搜过,直接跳过。
  • 然后,枚举下一个起点 (i+1, )。
  • 直到做到不重不漏。

Q1:数组定义疑惑:为什么 vis 用一维?
A1:想一下这个算法的递归过程:如果当前行的当前列合法,进入下一行,又因为当前列被搜索过,新行的这一列自然不能再搜索,即需要 y++

  • 如果 (x+1, ++y) 合法,进入下一列和下一列 (x+1, y++)
  • 如果 (x+1, ++y) 非法,当前行不变,进入检查下一列 (x, y++)
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int N, k;
	static char[][] grid;
	static boolean[] vis;
	static int res;
	
	static void dfs(int x, int cur) {
		if (x == N) {
			if (cur == k)
				res++;
			return;
		}
		for (int y = 0; y < N; y++) {
			if (vis[y] || grid[x][y] != '#' ) 
				continue;
			vis[y] = true;
			dfs(x+1, cur+1);
			vis[y] = false;
		}
		dfs(x+1, cur);
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		while (true) {
			N = sc.nextInt();
			k = sc.nextInt();
			if (N == -1 && k == -1)
				break;
			grid = new char[N][N];
			vis = new boolean[N];
			for (int i = 0; i < N; i++) {
				String s = sc.next();
				for (int j = 0; j < N; j++) {
					grid[i][j] = s.charAt(j);
				}
			}
			dfs(0, 0);
			System.out.println(res);
			res = 0;
		}
    }
}

复杂度分析

  • 时间复杂度: O ( 2 n ) O(2^n) O(2n)
  • 空间复杂度: O ( 2 n ) O(2^n) O(2n)

你可能感兴趣的:(#,【回溯】)