8.1搜索专练DFS和BFS
这是第一次全部做出来的依次练习了,有一些都是做过两遍了的,但是还是错了几回,更多时候我还是应该多注意下细节,就好像章爷笑我 的一样,像什么vis[]标记没清0,什么格式错误,还有什么题目没看清,还是的注意一下了。
地址: 8.1搜索练习
Problem A POJ 2488
题目大意就是说帮你给你个P*Q的棋盘,让马一次全部走完,每个点只能走一次,而且要按照字典序输出(这句话最坑!!!)。
这道题做过了两三次了,却老是被那句“字典序输出”给坑死。第一次看到这道题的时候,看到他说的字典序还以为是要让起点最小(虽然现在明白任何一个点作为起点都是可以到达终点的,只要里面有一条路可以遍历完),然后高高兴兴的枚举了起点,心想好简单叽里呱啦就敲完了,测试样例!过了!提交!WA了!然后就回头看代码,检查实在是无力了,在网上一搜,发现别人只用0,0,做起点就过了,马上改了提交!!!还是WA!!心力憔悴的我无力了,有重新找别人的代码,看到别人代码原来还有一小段注释
“int dir[8][2]={-2,-1,-2,1,-1,-2,-1,2,1,-2,1,2,2,-1,2,1}; /*注意此处的数组数据, 为了保证每次的探索都是 符合字典序的*/”
然后才慢慢明白什么叫字典序输出,其实就是要让每一步走的位置要是在能够遍历每个点的前提下,尽量让字典序小。大家可以慢慢体会下。
贴代码:180 KB 16 ms
1 #include <stdio.h> 2 #include <string.h> 3 int vis[30][30]; 4 5 const int d[8][2] = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}}; 6 int m,n,num,ans[900],IsFind; 7 8 9 void dfs(int x,int y) 10 { 11 if(x<0 || x>=m || y<0 || y>=n || vis[x][y] || IsFind) return ; 12 vis[x][y] = 1; 13 ans[num++] = x*n+y; 14 if(num == m*n){IsFind = 1; return;} 15 for(int i=0;i<8 && !IsFind;i++) 16 { 17 dfs(x+d[i][0], y+d[i][1]); 18 } 19 if(!IsFind) 20 { 21 num--; 22 vis[x][y]=0; 23 } 24 } 25 26 int main() 27 { 28 int Case,T=0; 29 while(~scanf("%d", &Case))while(Case--) 30 { 31 memset(vis,0,sizeof(vis)); 32 scanf("%d%d", &n, &m); 33 IsFind = 0; 34 num=0; 35 dfs(0,0); 36 printf("Scenario #%d:\n", ++T); 37 if(!IsFind)printf("impossible"); 38 else for(int i=0;i<num;i++) 39 { 40 printf("%c%d", ans[i]/n+'A', ans[i]%n+1); 41 } 42 printf("\n\n"); 43 if(!Case)T=0; 44 } 45 return 0; 46 }
Problem B Avoid The Lakes
简单的DFS求最大相连块 392 KB 16 ms
1 #include <stdio.h> 2 #include <string.h> 3 #include <queue> 4 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 5 using namespace std; 6 7 const int d[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; 8 int N,M,K; 9 int Map[105][105],vis[105][105]; 10 11 int DFS(int x,int y) 12 { 13 if(x<0 || x>=N || y<0 || y>=M || vis[x][y] || Map[x][y])return 0; 14 int area = 1; 15 vis[x][y] = 1; 16 for(int i=0;i<4;i++) 17 { 18 area += DFS(x+d[i][0], y+d[i][1]); 19 } 20 return area; 21 } 22 23 int main() 24 { 25 while(~scanf("%d%d%d",&N,&M,&K)) 26 { 27 memset(Map,1,sizeof(Map)); 28 memset(vis,0,sizeof(vis)); 29 int a,b; 30 for(int i=0;i<K;i++) 31 { 32 scanf("%d%d", &a,&b); 33 Map[a-1][b-1] = 0; 34 } 35 int ans = 0; 36 for(int i=0;i<N;i++) 37 { 38 for(int j=0;j<M;j++)if(!vis[i][j] && !Map[i][j]) 39 { 40 int temp = DFS(i,j); 41 ans = MAX(ans, temp); 42 } 43 } 44 printf("%d\n", ans); 45 } 46 return 0; 47 }
Problem C Dungeon Master
只是将求最短距离从二维增加到三维而已,数组加一维便可以实现436 KB16 ms
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 using namespace std; 5 6 const int Dir[6][3] = {{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}}; 7 struct node 8 { 9 int x,y,z; 10 int step; 11 }Start; 12 char Map[35][35][35]; 13 int vis[35][35][35]; 14 int L,R,C; 15 16 void readData() 17 { 18 memset(vis,0,sizeof(vis)); 19 int IsFind_S=0; 20 for(int i=0;i<L;i++) 21 { 22 for(int j=0;j<R;j++) 23 { 24 scanf("%s",Map[i][j]); 25 for(int k=0;k<C && !IsFind_S;k++) if(Map[i][j][k] == 'S') 26 { 27 Start.x = i; 28 Start.y = j; 29 Start.z = k; 30 vis[i][j][k] = 1; 31 Start.step = 0; 32 IsFind_S = 1; 33 } 34 } 35 } 36 } 37 38 int BFS() 39 { 40 queue<node>q; 41 q.push(Start); 42 while(!q.empty()) 43 { 44 node u = q.front(); 45 q.pop(); 46 int x = u.x, y = u.y, z = u.z; 47 if(Map[x][y][z] == 'E')return u.step; 48 for(int i=0;i<6;i++) 49 { 50 int nx = x + Dir[i][0], ny = y + Dir[i][1], nz = z + Dir[i][2]; 51 if(nx>=0 && nx<L && ny>=0 && ny<R && nz>=0 && nz < C && Map[nx][ny][nz]!='#' && !vis[nx][ny][nz]) 52 { 53 vis[nx][ny][nz] = 1; 54 node v; 55 v.x = nx; v.y = ny; v.z = nz; v.step = u.step+1; 56 q.push(v); 57 } 58 } 59 } 60 return 0; 61 } 62 63 int main() 64 { 65 while(scanf("%d%d%d%*c", &L, &R, &C) && (L||R||C)) 66 { 67 readData(); 68 int ans = BFS(); 69 if(!ans)printf("Trapped!\n"); 70 else printf("Escaped in %d minute(s).\n",ans); 71 } 72 return 0; 73 }
Problem D Sum It Up
题目意思就是说给一个T和N个数,让你求出所有的可以让N个数里面找出一些数使得他们的和为T,求所有情况
由于题目说了每给一个数,这个数就只能用一次,就是说帮你给20,20,20,那么你可以用1个,2个或者3个20,但是你只用第1个或者只用第2个这两种情况都是一样的。这样的话我们每次储存时候就不要重复的储存每个数,而是另外开一个数组保存每个不一样的数,在开一个数组保存每个数的次数。那么在DFS的时候就按照每个不同的数出现的次数,从max~0一直到他们的和为T,就打印数组
代码里还有注释:228 KB 0 ms
1 #include <stdio.h> 2 #include <string.h> 3 #include <queue> 4 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 5 using namespace std; 6 7 int ans[15]; 8 int N,T,IsFind; 9 int use[15],num[15],NumOfUse;//NumOdUse用来统计有多好个不同的数,Use放的是不同的数,num放的是每个不同的数出现的次数 10 int index,k;//index表示DFS到了use[index],ans总共有k个数 11 12 void DFS(int sum) 13 { 14 if(sum > T)return ;//和已经大于目标值,不需要继续搜索下去了 15 if(sum == T)//找到了就打印出来 16 { 17 IsFind = 1;//找到了 18 for(int i=0;i<k;i++)//打印 19 { 20 printf("%d%c", ans[i], (i==k-1)? '\n' : '+'); 21 } 22 return ; 23 } 24 if(index == NumOfUse)return;//已经找到了最后一个,任然不满足,返回 25 for(int i=num[index];i>=0;i--)//对于第num[index],枚举他出现的次数 26 { 27 for(int j=0;j<i;j++)ans[k+j] = use[index];//向ans里放入i个数 28 index+=1;k+=i; 29 DFS(sum+use[index-1]*i); 30 index-=1;k-=i; 31 } 32 } 33 34 int main() 35 { 36 while(~scanf("%d%d", &T,&N) && N) 37 { 38 memset(num,0,sizeof(num)); 39 memset(ans,0,sizeof(ans)); 40 memset(use,0,sizeof(use)); 41 scanf("%d", &use[0]); 42 num[0] = 1; 43 NumOfUse = 1; 44 int temp; 45 for(int i=1;i<N;i++) 46 { 47 scanf("%d", &temp); 48 if(temp == use[NumOfUse-1])num[NumOfUse-1]++;//由于题目保证输入时非升序,所以直接与前一个比较,相同,次数加1 49 else {use[NumOfUse] = temp; num[NumOfUse]++;NumOfUse ++;}//不同,数的个数加1 50 } 51 IsFind = 0; 52 printf("Sums of %d:\n",T); 53 DFS(0); 54 if(!IsFind)printf("NONE\n"); 55 } 56 return 0; 57 }
Problem E N皇后问题
第一次,直接用白书的方法,超时
1 #include <stdio.h> 2 #include <string.h> 3 int vis[3][25],ans,n; 4 5 void dfs(int cur) 6 { 7 if(cur == n){ans++;return;} 8 for(int i=0;i<n;i++) 9 { 10 if(!vis[0][i] && !vis[1][i+cur] && !vis[2][cur-i+n]) 11 { 12 vis[0][i] = vis[1][i+cur] = vis[2][cur-i+n] = 1; 13 dfs(cur+1); 14 vis[0][i] = vis[1][i+cur] = vis[2][cur-i+n] = 0; 15 } 16 } 17 } 18 19 20 int main() 21 { 22 while(~scanf("%d", &n) && n) 23 { 24 ans = 0; 25 memset(vis,0,sizeof(vis)); 26 dfs(0); 27 printf("%d\n", ans); 28 } 29 return 0; 30 }
第二次,听了章爷的说法,打表,没有写 if(!n)break;WA了
1 #include <stdio.h> 2 #include <string.h> 3 int ans[11] = {0,1,0,0,2,10,4,40,92,352,724}; 4 int main() 5 { 6 int n; 7 while(~scanf("%d", &n)) 8 { 9 printf("%d\n",ans[n]); 10 } 11 return 0; 12 }
第三次,终于过了228 KB15 ms
1 #include <stdio.h> 2 #include <string.h> 3 int ans[11] = {0,1,0,0,2,10,4,40,92,352,724}; 4 int main() 5 { 6 int n; 7 while(~scanf("%d", &n) && n) 8 { 9 printf("%d\n",ans[n]); 10 } 11 return 0; 12 }
Problem F Basic
此题我没有加入太多想法,有思路就敲了,所以写得有些挫,代码效率也不高,基本思路就是枚举每个点是否放上一个棋子,共有2^16种情况,然后就没放上一个就标记之后不能放上的点,找出者2^16中情况中可以放得数目最多的
其实也可以直接枚举每个点,放或者不放两种情况,搜索下一个点
代码 172 KB63 ms
1 #include <stdio.h> 2 #include <string.h> 3 #include <queue> 4 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 5 using namespace std; 6 7 int N; 8 char Map[5][5],vis[5][5]; 9 const int d[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; 10 11 int main() 12 { 13 while(~scanf("%d", &N) && N) 14 { 15 for(int i=0;i<N;i++) 16 { 17 scanf("%s", Map[i]); 18 } 19 int Max = 0; 20 for(int state = 1;state < (1<<(N*N));state++) 21 { 22 memset(vis,0,sizeof(vis)); 23 int ans = 0; 24 for(int i=0;i<(N*N);i++) 25 { 26 if(((1<<i) & state) && Map[i/N][i%N]!='X' && !vis[i/N][i%N]) 27 { 28 ans++; 29 vis[i/N][i%N] = 1; 30 for(int k=0;k<4;k++) 31 { 32 int x = i/N +d[k][0], y = i%N+d[k][1]; 33 while(x>=0 && x<N && y>=0 && y<N && Map[x][y]!='X') 34 { 35 vis[x][y] = 1; 36 x+=d[k][0]; y += d[k][1]; 37 } 38 } 39 } 40 } 41 Max = MAX(Max, ans); 42 } 43 printf("%d\n", Max); 44 } 45 return 0; 46 }
Problem GAsteroids!
简单的三维BFS问题
1 #include <stdio.h> 2 #include <string.h> 3 #include <queue> 4 #define Is(a,b) (a>=0 && a<b) 5 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 6 using namespace std; 7 8 const int Dir[6][3] = {{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}}; 9 int vis[15][15][15],N; 10 struct node{int x,y,z,step;}; 11 node Start,End; 12 char Map[15][15][15]; 13 14 int ReadData() 15 { 16 memset(Map, 0, sizeof(Map)); 17 char str[10]={0}; 18 scanf("%s %d%*c", str, &N); 19 for(int i=0;i<N;i++) 20 { 21 for(int j=0;j<N;j++) 22 { 23 scanf("%s", Map[i][j]); 24 } 25 } 26 int x,y,z; 27 scanf("%d%d%d",&x,&y,&z); 28 Start.x = z; Start.y = y; Start.z = x; 29 scanf("%d%d%d",&x,&y,&z); 30 End.x = z; End.y = y; End.z = x; 31 scanf("%s",str); 32 if(str[0] == 'E')return 1; 33 return 0; 34 } 35 36 int BFS() 37 { 38 memset(vis, 0, sizeof(vis)); 39 queue<node>q; 40 Start.step = 0; 41 q.push(Start); 42 node u, v; 43 vis[Start.x][Start.y][Start.z] = 1; 44 while(!q.empty()) 45 { 46 u = q.front(); 47 q.pop(); 48 int x = u.x, y = u.y, z = u.z; 49 if(x == End.x && y == End.y && z == End.z)return u.step; 50 for(int i=0;i<6;i++) 51 { 52 int nx = x+Dir[i][0], ny = y+Dir[i][1], nz = z+Dir[i][2]; 53 if(Is(nx,N) && Is(ny,N) && Is(nz, N) && !vis[nx][ny][nz] && Map[nx][ny][nz]!='X') 54 { 55 vis[nx][ny][nz] = 1; 56 v.x = nx; v.y = ny; v.z = nz; 57 v.step = u.step+1; 58 q.push(v); 59 } 60 } 61 } 62 return -1; 63 } 64 65 int main() 66 { 67 while(ReadData()) 68 { 69 int ans = BFS(); 70 if(ans == -1)printf("NO ROUTE\n"); 71 else printf("%d %d\n",N,ans); 72 } 73 }