这道题目与八皇后问题相似,唯一的区别就是n > m,这就使得棋盘中可能存在空行,即使某一行中有可以放的位置也不能选。这题的剪枝还是有很多可以考虑的。
我考虑了如下几个剪枝点:
1). 当剩余的可放位置小于剩余的要摆放棋子的个数的时候,就不需在继续搜索下去了,一定不会得到解。
2). 由于每一行每一列只能放置一个棋子,所以当剩余的行数小于剩余的棋子的个数的时候就不需要在搜索,一定得不到解。
代码如下:
#include<stdio.h>
#include<string.h>
bool row[10] ;
bool col[10] ;
int rnum[10] ;
int map[10][10] ;
int ncount ;
int n ;
int m ;
int nsum ;
int dfs(int pos , int num , int rest){
if(num == m){
ncount ++ ;
return 1 ;
}
int i ;
int j ;
if(rest < m - num){
return 0 ;
}
if(n - pos < m - num){
return 0 ;
}
for(i = pos + 1 ; i <= n ; i ++){
for(j = 1 ; j <= n ; j ++){
if(!map[i][j] && !row[i] && !col[j]){
map[i][j] = 1 ;
row[i] = 1 ;
col[j] = 1 ;
dfs(i , num + 1 , rest - rnum[i]) ;
map[i][j] = 0 ;
row[i] = 0 ;
col[j] = 0 ;
}
}
rest = rest - rnum[i] ;
if(rest < m - num){
break ;
}
if(n - i < m - num){
break ;
}
}
return 0 ;
}
void work() {
int i ;
int j ;
char str[10] ;
memset(map , -1 , sizeof(map)) ;
memset(row , 0 , sizeof(row)) ;
memset(col , 0 , sizeof(col)) ;
memset(rnum , 0 ,sizeof(rnum)) ;
nsum = 0 ;
ncount = 0 ;
for(i = 1 ; i <= n ; i ++){
scanf("%s" , str+1) ;
for(j = 1 ; j <= n ; j ++){
if(str[j]== '#'){
map[i][j] = 0 ;
rnum[i] ++ ;
nsum ++ ;
}
}
}
if(nsum < m){
printf("%d\n" , ncount) ;
return ;
}
for(i = 1 ; i <= n ; i ++){
for(j = 1 ; j <= n ; j ++){
if(!map[i][j] && nsum >= m){
row[i] = 1 ;
col[j] = 1 ;
map[i][j] = 1 ;
dfs(i , 1 , nsum - rnum[i]) ;
row[i] = 0 ;
col[j] = 0 ;
map[i][j] = 0 ;
}
}
nsum = nsum - rnum[i] ;
if(nsum < m){
break ;
}
if(m > n - i){
break ;
}
}
printf("%d\n" , ncount) ;
}
int main(){
while(scanf("%d %d" , &n , &m)!=EOF && n != -1 && m != -1){
work() ;
}
}