这道题目呢,思路是比较清晰的,首先肯定得用深搜,用两个标记数组,一个用来标记某一行是否已走,另一个用来标记某一列是否已走,然后在满足条件的情况下,搜出最多的可行方案。然而呢,我这个菜狗,竟然没把代码写好,改了好半天,还是WA,好弱啊。。。
(这段话,大家可以不用看了= =,刚开始的代码bug出现在哪呢,
4 4 ...# ..#. .#.. #...把满足条件的(0,3),(1,2),(2,1),(3,0)跑完了之后呢,看最后一行还有没有其他的点满足条件的,确认没有之后呢,回溯到倒数第二行,在这之前呢,把(3,0)这个点呢,恢复到可走状态,然后在倒数第二行,没发现除(2,1)之后的其他点,因为我在每一个dfs里面呢,都是两层for循环,所以就跑到了下一行,最后又得到了一组“正确的”结果(0,3),(1,2),(3,0),(2,1)
后来呢,我又开了一个标记数组,用来标记某个点是否已经走过,这样的话,就不会出现上面的情况了,因为(3,0)这个点已经走过了,所以不会再走了。。。。但是呢,又出现了一个问题,就是如果在倒数第二行除了(2,1)点之外呢,还有满足条件的点,那么由于换了点,所以最后一层的点应该要重新和这个点结合一起看是否满足题目条件,但是呢由于最开始最后一层的点已经走过,被标记为不可走了,所以呢,这种情况下,也不会走。。。。。想了半天,不得其解,我好弱啊= =
)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 10;
char ch[maxn][maxn];//存棋盘
int vis[maxn];//vis[i],用来标记第i列是否可走
int n,m;//n行n列,m个棋子
int cnt,num;//cnt是最后可行的方案数,num是当前已经有几个棋子下好了
void dfs(int i)//i表示第i行
{
if (num == m)
{
++cnt;//如果当前已经有m个棋子下好了,说明这种方案可行
return ;
}
if (i >= n)//n行的图,超过了n-1,就不在图范围内了
return ;
for (int j = 0; j < n; ++j)//遍历某一行的所有列
{
if (!vis[j] && ch[i][j] == '#')
{
vis[j] = 1;
++num;
dfs(i+1);
vis[j] = 0;//回溯的时候,这列重新可走
--num;//回溯
}
}
dfs(i+1);//对于这一条语句的作用,可以这样理解,如果有一行(不是最后一行),
//字符全部都是.,没有#,如果没有这一条语句,那么由于没有#,所以在跑上面的for
//循环时,不会递归到下一行,跑完了for循环就回溯到上一行了,但事实上呢,这一行
//的下面还有图,但是都不会再跑,所以这就是这一条语句的作用
}
int main()
{
int i,j;
while (~scanf("%d%d",&n,&m))
{
if (n == -1 && m == -1)
break;
for (i = 0; i < n; ++i)//输入棋盘
{
getchar();
for (j = 0; j < n; ++j)
scanf("%c",&ch[i][j]);
}
memset(vis,0,sizeof(vis));//刚开始每一列都可下棋子
cnt = 0;
num = 0;
dfs(0);
printf("%d\n",cnt);
}
return 0;
}
/*
这段代码是怎么解决我之前的那两个bug的呢?
首先这里的dfs搜的是特定的某一行,并且不回溯的话,行是不会减小的,也就是说,对于
(0,3),(1,2),(3,0),(2,1)这段bug结果,如果找到了(3,0)点,是不会再找到
(2,1)这个点的(仔细体会上面的代码过程!)
第二个bug的解决是回溯的时候,列重新标记为可走状态!
感觉这段代码最重要之处,就是它的每一次dfs只是遍历特定的某一行
*/