基础算法之DFS应用

    xiaoxiao2021-03-26  33

    深度优先搜索

    POJ 1979 Red and Black

    简单深搜,题意很简单,”#”代表墙,”.”可以走,”@”代表出发点,问最多能到达的方格的最大面积(不走重复)。 其中DFS内部,开始因为这样的错误找不到问题在哪。。

    for(i = 0; i<4; i++){ m += dir[i][0]; n += dir[i][1]; dfs(m,n); }

    错误原因:应在同一个位置上向下左上右试探,函数调用退栈回到当前位置时,p、q的值已经发生了改变。 修改后AC代码:

    #include <iostream> #include <stdio.h> #include <string.h> using namespace std; char map[21][21]; int dir[][2] = {{1,0},{0,-1},{-1,0},{0,1}}; int res,W,H; int dfs(int p,int q){ int i,m,n; //printf("p = %d,q = %d ",p,q); if(p>=0 && q>=0 && p<H && q<W && map[p][q]=='.'){ map[p][q] = '#'; res++; } else //(map[i][j]=='#') return 0; for(i = 0; i<4; i++){ m = p + dir[i][0]; n = q + dir[i][1]; dfs(m,n); } return 0; } int main() { int i,j,p,q; while(scanf("%d%d",&W,&H)!=EOF && W!=0){ res = 0; getchar(); for(i = 0; i<H; i++){ for(j = 0; j<W; j++){ scanf("%c",&map[i][j]); if(map[i][j]=='@'){ p = i; q = j; map[p][q] = '.'; } } getchar(); } dfs(p,q); printf("%d\n",res); }//for i return 0; }

    HDU 2553 N皇后问题

    一类典型的回溯方法应用问题,转两种高效算法题解(分别用递归、非递归)。 http://blog.csdn.net/hackbuteer1/article/details/6657109

    回溯算法的核心思想:但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择 首先写的非递归方法,Time Limited.

    #include <iostream> #include <stdio.h> #include <string.h> #include <cmath> using namespace std; int res; int map[22],N; int canPlace(int row){ int i; for(i = 0; i<row; i++) if( (abs(i-row) == abs(map[i]-map[row]))||( map[row] == map[i]) ) //得出正确结果的关键:准确找到规律,验证 return 0; return 1; } void queen(int row){ int i; if(row == N){ res++; } else for(i = 0; i<N; i++){ map[row] = i; if(canPlace(row)){ queen(row+1); //思考(row+1) } } return ; } int main() { int i,j; while(scanf("%d",&N)!=EOF && N!=0){ res = 0; memset(map,0,sizeof(map)); queen(0); printf("%d\n",res); } return 0; }

    注释处不能用queen(row++),原因是回溯到该位置时,这样的话row的值已经发生变化,并不是回溯到原状态,此处的问题和上一道题出现的问题类似,以后一定要注意。

    非递归代码:

    #include <iostream> #include <stdio.h> #include <string.h> #include <cmath> #define INITIAL 10000 using namespace std; int res; int map[22],N; int canPlace(int row,int loc){ int i; for(i = 0; i<row; i++) if( (abs(i-row) == abs(map[i]-loc))||( loc == map[i]) ) return 0; return 1; } void queen(){ int i = 0,j = 0; //当前行,当前列 while(i < N){ while(j < N){ if(canPlace(i,j)){ map[i] = j; //回溯到第一行,仍然无法找到可以放置皇后的位置,则说明已经找到所有的解,程序终止 j = 0; break; } else{ j++; } } if(map[i] == INITIAL){ if( i == 0) //回溯到第一行,仍然无法找到可以放置皇后的位置,则说明已经找到所有的解,程序终止 break; else{ i--; j = map[i]+1; map[i] = INITIAL; continue; } } if(i == N-1){ //最后一行找到了一个皇后位置,说明找到一个结果 res++; //不能在此处结束程序,因为我们要找的是N皇后问题的所有解,此时应该清除该行的皇后,从当前放置皇后列数的下一列继续探测。 j = map[i]+1; map[i] = INITIAL; continue; } ++i; } } int main() { int i,j; while(scanf("%d",&N)!=EOF && N!=0){ res = 0; memset(map,INITIAL,sizeof(map)); queen(); printf("%d\n",res); } return 0; }

    改为非递归方法实现,依然TimeLimited。 百度研究了一下,发现好多人遇到同样的问题,最后选择打表。

    #include<stdio.h> int main() { int a[11]={0,1,0,0,2,10,4,40,92,352,724},n; while(scanf("%d",&n)!=EOF&&n) { printf("%d\n",a[n]); } return 0; }

    不打表貌似只能用位运算。位运算部分过后分专项出来学习。

    HDU 1426 Suduku Killer

    解数独,值得认真思考,这里附上一个很好的题解。 http://blog.csdn.net/riba2534/article/details/53526709 题解中的代码也是可以AC的。 自己写的非递归代码过了样例,找不到WA的原因,后来发现如果遇到障碍需要回退时,自己的算法完全就是有漏洞的,会无限循环,而样例中是难得顺利的一次过,这是非常巧合的。 后来参考了题解中的代码写了递归的方法,终于AC。

    #include <stdio.h> #define MAX 9 int map[MAX+1][MAX+1]; int a[100][2]; int num; int ifOk(int x, int y, int n){ int x1,x3,y1,y3,i,j; for(i = 0; i<MAX; i++) if(map[x][i] == n) return 0; for(i = 0; i<MAX; i++) if(map[i][y] == n) return 0; x1 = x/3*3; x3 = x1+3; y1 = y/3*3; y3 = y1+3; for(i = x1; i<x3; i++) for(j = y1; j<y3; j++) if(map[i][j]==n) return 0; return 1; } void dfs(int step){ int i,j; if(step==num){ //为什么如果在main中输出?位没有数值都是零呢? for(i = 0; i<MAX; i++){ for(j = 0; j<MAX-1; j++){ printf("%d ",map[i][j]); } printf("%d",map[i][j]); printf("\n"); } return ; } for(j = 1; j<=MAX; j++){ if(ifOk(a[step][0],a[step][1],j)){ map [a[step][0]] [a[step][1]] = j; dfs(step+1); map[a[step][0]] [a[step][1]] = 0; } }//for j return; } int main(){ char ch[2]; int i,j,cas=0; while(scanf("%s",&ch)!=EOF){ num = 0; if(ch[0]!='?') map[0][0] = ch[0]-'0'; else{ a[num][0] = 0; a[num][1] = 0; map[0][0] = 0; num++; } for(i = 1; i<MAX; i++){ scanf("%s",&ch); if(ch[0]!='?') map[0][i] = ch[0]-'0'; else{ a[num][0] = 0; a[num][1] = i; map[0][i] = 0; num++; } } for(i = 1; i<MAX; i++) for(j = 0; j<MAX; j++){ scanf("%s",&ch); if(ch[0]!='?') map[i][j] = ch[0]-'0'; else{ a[num][0] = i; a[num][1] = j; map[i][j] = 0; num++; } } //题目这输出方式很奇怪啊,打比赛让自己想我肯定想不到,还要多做题呀 if(cas++) printf("\n"); dfs(0); }//while return 0; }

    自己在网上找了一道数独题目改成了要求的格式,也可以给大家验证自己的代码: ? 6 1 ? 3 ? ? 2 ? ? 5 ? ? ? 8 1 ? 7 ? ? ? ? ? 7 ? 3 4 ? ? 9 ? ? 6 3 7 8 ? ? 3 2 7 9 5 ? ? 5 7 ? 3 ? ? 9 ? 2 1 9 ? 7 6 ? ? ? ? 8 ? 2 4 ? ? 7 6 ? 6 4 ? ? 1 ? 2 5 ?

    转载请注明原文地址: https://ju.6miu.com/read-661919.html

    最新回复(0)