传送门:http://poj.org/problem?id=1321
分析:
此题与8皇后问题类似,关键如下:
第一、并不是每一个格子都可以放棋子
第二、并不是对应n行n列的棋盘一定要放n个棋子
第三、约束条件较八皇后问题少了必须不能再同一斜线上
综上,此题在八皇后问题的基础上需要修改的重要点有:
第一、在backtrace函数中,当已经放置的棋子数达到k个时,需要返回;当到最底层时,需要返回。
第二、此时的Loca数组没有必要再去记录第i行的棋子放置位置,因为有可能这一行根本就不放棋子。
在注意了上述事项后,此题的思路就很清晰了:
从第一行第一列开始,判断此行列能否放置棋子,如果可以,我们有两种选择,放或不放,每种情况都需要对相应的sum和loca作修改,loca[i]=0表示第i列还没有放置,=1则表示已经放过棋子了,这样就可以得到剪枝函数为loca[i]=0且input[m][i]='#'。需要特别注意的是,如果不放棋子的话,backtrace函数是可以放在for循环内的每一次改变后,也可以放在for循环外,但是在前者会做很多无用功,增加了回溯的次数,造成超时,无法通过,当我把这个backtrace放在外面时,POJ就提示AC了。
代码如下:
#include <stdio.h>
char input[10][10];
int sum , num ;
int n , k ;
int loca[10];//loca[i]=0表示第i列没有放置棋子,=1表示放了棋子
int place(int p , int q)
//判断p行q列能否放棋子的关键
//第一是第q列是否放过棋子
//第二是第p行q列是否为#
{
return loca[q]==0&&input[p][q]=='#';
}
void backtrace(int t)
{
int i;
if(num == k)
sum++;
else
if(t >= n)
return ;
else
{
for(i = 0 ; i < n ; i ++)
{
if(place(t,i))//如果第t行第i列可以放置
{
num ++ ;//放过的棋子增1
loca[i] = 1;//表示第i列放过棋子了
backtrace(t+1);//如果在这一列放棋子的话则按此纵深
num --;
loca[i] = 0;//表示在这一列不放棋子
//backtrace(t+1);
}
}
backtrace(t+1);
}
}
int main()
{
int i;
while(scanf("%d%d",&n,&k)==2)
{
if(n==-1&&k==-1)
return 0;
for(i = 0 ; i < n ; i ++)
scanf("%s",input[i]);
memset(loca,0,sizeof(loca));//对loca函数置零,表示每一列都还没有放
backtrace(0);
printf("%d\n",sum);
sum = 0;
num = 0;
}
return 0;
}