虽然学习了数据结构,但总感觉学得很浅,浮于表面具体应用还是不太会,所以开始刷OJ了,希望能有点收获吧。在ZOJ上刷,1001没啥可说的,就决定从1002开始做起了。题目就不多说了,直接开始代码吧。
这道题要求的数据量不是很大,所以直接穷举所有可能性应该也行得通,这里主要用了深度优先算法,(话说数据结构的时候都没想到这个算法这么有用,光学不实践是真的是掌握不了),基本思想就是可以把整个过程想成一个类似树的结构(个人看法:感觉这样比较好理解),图中的每个格子看作树的一个节点,从第一个格子开始深度搜索,16个节点全部搜索完后,回溯到该节点,如果该节点设了碉堡,则变为没设碉堡的情况,从另一种可能情况的分支开始新一轮搜索,以此穷尽所有可能的情况,搜索的过程中保存本轮搜索的碉堡数,各次比较后,取得最大值。
具体依代码讲解如下:
先上main函数吧:
int main(void) {
ios_base::sync_with_stdio(false);
while (cin >> n && n) { //等价于读取n且判断n是否等于0,若为0则退出循环
for (int i = 0; i < n; i++)
cin >> Map[i];
ans = 0; //初始化碉堡数为0
dfs(0, 0); //从第一个格子开启深度优先搜索
cout << ans << endl; //递归搜索结束后,输出碉堡最多的个数
}
//system("pause");
return 0;
}
main函数没有啥太重要的,结合着注释看应该ok的
然后就是最重要的核心代码dfs深度优先搜索代码,代码如下:
void dfs(int num, int ans_temp) { //深度优先搜索,用1个num就能实现对16个格的遍历搜索,行用除法得到,列用取模得到
if (num == n * n) {
ans = max(ans, ans_temp);
return;
}
else {
int x = num / n; //得到行号
int y = num % n; //得到列号
if (Map[x][y] == '.'&&judge(x, y)) {
Map[x][y] = 'C'; //设置为碉堡
dfs(num + 1, ans_temp + 1);//碉堡总数,格子数加1,深度优先搜索下一格子
Map[x][y] = '.';//实现回溯,如果不在这里建碉堡的情况分支
}
//else
dfs(num + 1, ans_temp);//遍历下一格子
}
}
这里几个要点,列在下面:
1.关于judge函数,其功能就是判断当前的x,y格子能不能放碉堡,具体内容会在下面指出来;
2.关于行号,列号的判断是在别的文章里看到的,直接设置一个总数num,用除法得到当前行号,用取模运算得到当前列号,刚开使在dfs函数中我设置了三个参数,其中两个分别传递行和列,但后来感觉这种方法更好,减少了传递的参数,同时递归出口的判断更为简单;
3.开始时,倒数第四行用了if-else的结构,但发现结果出错,原因在于这里不应该是一个单纯的分支结构,因为当if中一轮搜索结束时,节点重新被赋值为“.”(不建立碉堡)后,还要开始新一轮的递归搜索,如果用了else则无法开始新的一轮搜索;
4,如果对dfs搜索或递归不太理解,建议在dfs函数处设置断点,进行逐语句的调试,看num值的变化过程,应该会有帮助。
最后是judge函数的内容:
bool judge(int x, int y) {
for (int i = x - 1; i >= 0; i--) {//对所在列判断,看是否合法可建立碉堡
if (Map[i][y] == 'C') //该列已有碉堡且没有墙间隔,改点不合法无法建碉堡
return false;
if (Map[i][y] == 'X') //检测到墙,则不管该列前面有没有碉堡,都没有影响,列合法
break;
}
for (int i = y - 1; i >= 0; i--) {//对所在行判断,看是否合法,思想类似列
if (Map[x][i] == 'C')
return false;
if (Map[x][i] == 'X')
break;
}
return true;
这里有个小点,就是遍历时只对该格子前面的格子进行判断,因为按顺序进行深度搜索,该点之后的格子默认为还没有进行过搜索,不在考虑之列。
ok,1002就这么搞定了,耶!!!!