ps:流水账,不喜别看,然后内心咕哝骂我一句说我啰嗦,看在我写那么长的份上也别实际喷…
谢谢…
深搜根本没学会,这次a出来是因为之前一个月就把这个题做过。
(我那么笨,当然是看完别人的思路和别人的代码然后想了两天,几乎把别人代码背出来才把那个题a出来的啊…)
然后又重新写,错了好多次…所以说思路还是有点混乱,不能说我会了。
所以趁现在思路清晰赶快写一写思路!或许以后就明白怎么回事了,然后就算不明白重新看一看也就会了!
深度优先搜索DFS,从某个状态开始,不断地转移直到无法转移,然后退回到前一步的状态,继续转移到其他状态,如此不断重复,直至找到最终解。
首先,找那个无法转移的状态,假如是一个n * n 的矩阵,所以一共有 n 行。然后设有k枚棋子。设方案数为cnt (cnt一开始为0)。
在看一遍定义:
深度优先搜索DFS,从某个状态开始,不断地转移直到无法转移,然后退回到前一步的状态,继续转移到其他状态,如此不断重复,直至找到最终解。
好了,下面就找条件。
首先找清楚无法转移的这个条件:
无法转移有两种情况。
第一种无法转移的状态是棋子全放完了,即 k == 0;
棋子都放完啦,还往下找干哈???
这不仅是无法转移的状态,这时候棋子的分布也是方案的一种,毕竟棋子都放完了。
第二种是从最后一行即第n行往上找,下一次找的是n-1行,然后n - 2…直到…
找到第 1 行后 ,就把整个矩阵遍历完了,在下面就是 n - 1 = 0。没有东西啦!!!
这个时候就真是无法转移的状态!
刚刚的那个可能没棋子了还能继续找矩阵剩下的部分,这个连剩下的部分都没得。
所以可以先写出这样几行代码
void dfs(int n, int k){
if(K == 0){ cnt++; return; }//棋子为0,方案数+1,返回上一步。
if(n == 0) return;//第0行什么也没有,返回上一步。
......//待续
}
在看一遍定义:深度优先搜索DFS,从某个状态开始,不断地转移直到无法转移,然后退回到前一步的状态,继续转移到其他状态,如此不断重复,直至找到最终解。
接下来,对于第n行,有k这个棋子即 dfs(n , k)这个状态时,有两种选择,放,与不放。
不放就是从dfs(n , k)这个状态 -----> dfs(n - 1 , k )这个状态。向下找了一行,但是棋子数目不变。
放的话就是dfs(n , k) ----- > dfs(n - 1 , k - 1)棋子少了一个,然后又向下找了一行。毕竟一行只能放一个棋子。
然后到下一个状态还是有这两个选择,放或者不放…递归 , 递归…直到无法转移的状态为止。
(就是刚刚上面的两种情况啊 n == 0 || k == 0)的时候。 )
对于不放的时候要报证 n > k
因为假设n < k, n = 2 , k = 3 。只有两行, 你有三个棋子,题意是一行放一个。两行全放完还剩一个,怎么放都不能使棋子完全放完。这样转移到的不放状态就没意义了,所以前提是 n > k 时,才有不放的权力。
而对于放的时候,放完,矩阵的那一列就不能再放棋子了,所以要把那一列进行标记。
具体看看代码吧。
//本来写的小n然后发现这个n随着函数的变化在变化!!!!(作者自己理解,读者请无视)
int N; //矩阵大小
bool flag[10] = {false};//首先每一列的标记都是false,表示都没有放过棋子。
void dfs(int n , int k ){
if( k == 0 ) {cnt ++ ; return ;}
if( n == 0) return;
if( n > k ) dfs( n - 1 , k ); //转移到不放的状态
//转移到放的状态
for(int i = 1 ; i <= N ; i++ ){
//从本行找哪一列有棋盘区域。即有‘#’mp[n][i]的位置
if( mp[n][i] == '#' && flag[i] == false )//而且这一列flag为false没有放过棋子
//才能转移到放棋子的状态
{
flag[i] = true; //这一列标记放过了。然后棋子-1,行数-1进入下一个状态。
dfs( n - 1 , k - 1 );
line[i] = false;//退回该状态时,把标记初始化,棋子没有放过。
//这一步可能有点难想,画画图仔细思考一下!!
}
}
return ;
}
可能写的越长,你们就会觉得不好理解,不过作为作者的我是把能够想到的全写出来了,dfs这个算法让我头疼的是没有模板,遇到问题还是要自己去判断条件,自己去找能够转移的状态,去找无法转移的状态…然后每个状态在转移的过程中又会有限制条件。
所以我觉得这个算法还是挺难的…对目前的我来说。以后这方面的题要经常练!
对啦,还有一个注意的一点的是…数组下标为0,然后到第0行其实还有,然后就导致第n行没有(从0开始的话到n数组越界呀),写dfs就很不方便…为了让第0行没有,第n行有。
所以在构建棋盘图的时候就这样构造:
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j++)
cin >> mp[i][j];
下标从1开始到n结束,这样就能构成一个 n * n 的矩阵了。
(说笑呢…看的别人…学习了一下啦)
下面是ac代码:
#include <iostream>
#include <cstring>
using namespace std;
bool flag[10];
char mp[10][10];
int n,k,cnt;
int N;
void dfs(int n, int k)
{
if(k == 0) { cnt++; return; }
if(n == 0) return;
if(n > k) dfs(n-1, k);
for(int i = 1; i <= N; ++i)
if(mp[n][i] == '#' && flag[i] == false)
{
flag[i] = true;
dfs(n-1,k-1);
flag[i] = false;
}
return;
}
int main()
{
while(cin >> n >> k )
{
N = n;
if(n == -1 && k == -1) break;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
cin >> mp[i][j];
memset(flag,false,sizeof(flag));
cnt = 0;
dfs(n,k);
cout << cnt << endl;
}
return 0;
}
啊…刚刚又出bug了…bug就是刚刚那个作者理解的话