[回溯算法]棋盘问题

棋盘问题

题目描述

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

关于输入

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

关于输出

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

例子输入

2 1
#.
.#
4 4
…#
…#.
.#…
#…
-1 -1
例子输出

2
1
解题分析

这是一个典型的回溯问题,也被称为DFS(深度优先搜索)问题。我们需要在棋盘上放置k个棋子,每个棋子不能在同一行或者同一列。这个问题的解决方法是使用DFS搜索所有可能的放置方法,并在每次放置棋子时检查是否满足条件。

这段代码的主要思路如下:

  1. Go 函数是回溯的主体部分。它接受两个参数:当前行 row 和当前已放置的棋子数 step。如果 step 大于 k,说明已经找到了一种放置方法,因此增加方案数 C 并返回。如果 row 大于等于 n,说明已经遍历完所有的行,因此直接返回。然后,遍历当前行的每一列,如果当前位置可以放置棋子(即 board[row][j]#),并且这一列没有放置过棋子(即 col[j]0),则在这个位置放置一个棋子,并标记这一列已经放置过棋子。然后,递归调用 Go 函数,进入下一行,并增加已放置的棋子数。在返回之前,取消当前位置的棋子,并标记这一列没有放置过棋子。这是回溯的关键步骤,它使得在每一行都尝试过所有可能的放置方法后,可以回到上一行并尝试其他的放置方法。

  2. main 函数是程序的入口点。它首先读入棋盘的大小 n 和要放置的棋子数 k。然后,清空方案数 C 和列标记 col。接着,读入棋盘的形状,并调用 Go 函数开始回溯。最后,输出方案数 C

这段代码的时间复杂性是 O(n^n),因为在最坏的情况下,需要遍历棋盘上的每一行,并在每一行上尝试所有可能的放置方法。但是,由于棋盘的大小 n 不会超过8,因此这个时间复杂性在实际中是可以接受的。

#include 
#include 

using namespace std;

char board[9][9]; bool col[9];
int n,k; int C;

void Go(int row,int step){
    if(step>k){
        C++; return;
    }
    if(row>=n) return;
    for(int j=0;j<n;j++){
        if(col[j]==0 && board[row][j]=='#'){
            col[j]=1;
            Go(row+1,step+1);
            col[j]=0;
        }
    }
    Go(row+1,step);
}

int main() {
    while(cin>>n && cin>>k){
        if(n==-1 && k==-1) break;
        C=0; memset(col,0,sizeof(col));
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++){
                cin>>board[i][j];
            }
        Go(0,1);
        cout<<C<<endl;
    }
	return 0;
}

你可能感兴趣的:(算法,c++,开发语言)