问题
323:棋盘问题
总时间限制: 1000ms 内存限制: 65536kB
描述
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放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
理解
- 问的只是方案数
- “# 表示棋盘区域, . 表示空白区域”,这句容易产生歧义。棋盘不是完整的矩形,中间有缺失部分。#是棋盘的空白区域,.是缺失部分(可以理解为一块洞)。
- 跟八皇后差不多。八皇后是8个棋子,该次是棋子少于等于行数。每一行开始,放完k个棋子就是一种方案。
代码
#include <bits/stdc++.h>
using namespace std;
int n,m,he;
char c[10][10];
bool k[10];//记住哪一列用过了
void go(int hx,int mx){//递归,从哪一行开始,放了几个棋子
if(mx>m){he++;return;}//递归出口,放了m个棋子就多种方案数
for(int i=hx;i<=n;i++)//从hx行开始逐一尝试从余下行开始
for(int j=1;j<=n;j++)//每个列都尝试
if(!k[j]&&c[i][j]= =‘#’){//该列空,且非缺失部分,可以放
k[j]=1;//该分支递归结束前都要标记
//view(1);
go(i+1,mx+1);//hx+1的话,就是hx+1重复n次
k[j]=0;//回溯,取消标记
}
}
int main(){
//freopen(“data.cpp”,“r”,stdin);
while(cin>>n>>m&&!(n==-1&&m==-1)){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>c[i][j];//直接输入字符
}
}
//view(0);
memset(k,0,sizeof(k));
he=0;go(1,1);
cout<<he<<endl;
}
return 0;
}
小结
- 深搜要用递归
- 递归貌似循环,其实要在深一层进行。就像要做饭,先有个整体思路,发现没菜,就先去找到菜,再去进一步。是完全符合代码的顺序结构,但是递归是在深一层进行,递归回来了再继续剩余操作。
- 八皇后是要遍历每列,完了递归下一行。因为只能从第一行开始。
- 该题是每一行都可以开始。