- 今天主要回顾一下几个搜索
- DFS ——Depth First Search
- BFS ——Breadth First Search
- A*
- 迭代加深搜索
今天DFS和BFS的实现就不细讲了
我们先直接看A*算法的实现(python风格为主...带一点伪代码)
1 def A_star_search 2 q = PriorityQueue() 3 #优先队列,顺便起到open表的作用 4 q.put(qnode) 5 came_from = {} 6 #came_from为记录拓展过程的链表 7 cost_so_far = {} 8 #cost_so_far[p]为起点到节点p目前已知花费 9 came_from[start_state] = None 10 cost_so_far[start_state] = 0 11 12 while not q.empty(): 13 cur_state = q.get() 14 if cur_state == goal_state: 15 break 16 #假设用邻接表存储,用边权矩阵原理类似 17 for nxt in G.neighbour(cur_state): 18 new_cost = cost_so_far[cur_state] + G.cost(cur_state, nxt) 19 if nxt not in cost_so_far or new_cost < cost_so_far[nxt] 20 cost_so_far[nxt] = new_cost 21 priority = new_cost + heuristic(goal_state, nxt) 22 q.put(qnode(....including "priority", "nxt")) 23 came_from[nxt] = cur_state 24 25 return came_from, cost_so_far
核心思想在已知代价g的基础上引入估价函数h,由f=g+h确定搜索的顺序
无论是DFS还是BFS还是启发式搜索,关键在于,搜索中扩展顺序的确定
DFS和BFS都不在乎边权,而都是以层次为划分进行搜索,DFS的搜索顺序是深层节点优先,BFS的搜索顺序是同层节点优先
而A*搜索主要考虑边权和代价,由已知代价评估出“最有可能成为答案”的节点进行拓展
下面是一些具体题目的实现,主要根据vjudge上的一套习题为顺序
A-棋盘问题(poj 1321)
就是在给定形状的棋盘上问有多少种方式可以不同行不同列地摆放棋子
枚举每个可摆放区域摆或不摆即可
1 #include2 #include 3 #include 4 using namespace std; 5 6 const int N = 10; 7 8 struct node{ 9 node(){} 10 node(int x_, int y_){ 11 x=x_; 12 y=y_; 13 } 14 int x,y; 15 }D[N]; 16 int k,n,cnt,ans; 17 bool use[N]; 18 19 bool cmp(node a,node b) 20 { 21 return a.x<b.x; 22 } 23 24 void dfs(int i, int step) 25 { 26 if(step == k) 27 { 28 ans++; 29 return; 30 } 31 use[D[i].y] = 1; 32 for(int t=i+1; t<=cnt-k+1+step; t++) 33 { 34 if(use[D[t].y]||D[t].x == D[i].x) continue; 35 dfs(t, step+1); 36 } 37 use[D[i].y] = 0; 38 } 39 40 int main() 41 { 42 freopen("poj1321.in","r",stdin); 43 44 char ch; 45 46 while(1) 47 { 48 cnt = 0; 49 ans = 0; 50 memset(use,0,sizeof(use)); 51 52 scanf("%d %d", &n, &k); 53 if(n==-1 && k==-1) break; 54 for(int i=1;i<=n;i++) 55 for(int j=1;j<=n;j++) 56 { 57 scanf(" %c",&ch); 58 if(ch == '#') D[++cnt]=node(i,j); 59 } 60 sort(D+1,D+cnt+1,cmp); 61 for(int i=1; i<=cnt-k+1; i++) dfs(i,1); 62 printf("%d\n",ans); 63 } 64 return 0; 65 }
B-Dungeon Master (poj 2251)
给定一个3D的监狱地图,问你有没有办法逃出来,如果有,问最短路径
很基础的BFS
1 /* 2 L level 3 R row 4 C colomn 5 */ 6 7 #include8 #include 9 #include 10 using namespace std; 11 12 const int N = 30 + 5; 13 int ans; 14 int map[N][N][N]; 15 int l,r,c; 16 int sl,sr,sc; 17 int tl,tr,tc; 18 int u[6]={ 1,-1, 0, 0, 0, 0}; 19 int v[6]={ 0, 0, 1,-1, 0, 0}; 20 int w[6]={ 0, 0, 0, 0, 1,-1}; 21 bool inq[N][N][N]; 22 struct qnode{ 23 qnode(){} 24 qnode(int _l,int _r,int _c,int _step) 25 { 26 l = _l; r = _r; c = _c; step = _step; 27 } 28 int l,r,c,step; 29 }; 30 31 void bfs() 32 { 33 queue q; 34 int curl,curr,curc; 35 int dl,dr,dc; 36 37 q.push(qnode(sl,sr,sc,0)); 38 inq[sl][sr][sc]=1; 39 while(!q.empty()) 40 { 41 qnode cur = q.front(); 42 q.pop(); 43 for(int i=0;i<6;i++) 44 { 45 dl = cur.l + u[i]; 46 dr = cur.r + v[i]; 47 dc = cur.c + w[i]; 48 if (dl<1 || dl>l || dr<1 || dr>r || dc<1 || dc>c) continue; 49 if (!map[dl][dr][dc]) continue; 50 if (dl == tl && dr == tr && dc == tc) 51 { 52 ans = cur.step + 1; 53 return; 54 } 55 if (!inq[dl][dr][dc]) 56 { 57 inq[dl][dr][dc] = 1; 58 q.push(qnode(dl,dr,dc,cur.step+1)); 59 } 60 } 61 } 62 } 63 64 int main() 65 { 66 #ifndef ONLINE_JUDGE 67 freopen("poj2251.in","r",stdin); 68 #endif 69 char ch; 70 71 while(1) 72 { 73 memset(inq,0,sizeof(inq)); 74 memset(map, 0, sizeof(map)); 75 ans = -1; 76 77 scanf("%d %d %d",&l,&r,&c); 78 if(l==0 && r==0 && c==0) break; 79 for(int i=1;i<=l;i++) 80 for(int j=1;j<=r;j++) 81 for(int k=1;k<=c;k++) 82 { 83 scanf(" %c",&ch); 84 if(ch != '#') map[i][j][k] = 1; 85 if(ch == 'S') sl=i,sr=j,sc=k; 86 if(ch == 'E') tl=i,tr=j,tc=k; 87 } 88 bfs(); 89 if (ans!=-1) 90 printf("Escaped in %d minute(s).\n",ans); 91 else printf("Trapped!\n"); 92 } 93 return 0; 94 }
C-Catch that cow(poj 3278)
数轴上有两个点分别为n,k
有两种操作,当前坐标加一减一,或者坐标乘二,都耗费一单位时间
求耗费最少的时间是多少
枚举每一个时间的操作进行BFS
1 #include2 #include 3 #include 4 using namespace std; 5 6 const int N = 100000 + 10; 7 8 int n,k; 9 int ans; 10 int f[N]; 11 struct qnode{ 12 qnode(){} 13 qnode(int _n,int _step){ 14 n=_n; step=_step; 15 } 16 int n,step; 17 }; 18 19 void bfs() 20 { 21 memset(f, -1, sizeof(f)); 22 23 f[n] = 0; 24 queue q; 25 q.push(qnode(n,0)); 26 27 while(!q.empty()) 28 { 29 qnode cur = q.front(); q.pop(); 30 if (cur.n-1>=0 && f[cur.n-1]==-1) 31 { 32 f[cur.n-1] = cur.step + 1; 33 q.push(qnode(cur.n-1, cur.step + 1)); 34 } 35 if (cur.n+1<=k && f[cur.n+1]==-1) 36 { 37 f[cur.n+1] = cur.step + 1; 38 q.push(qnode(cur.n+1, cur.step + 1)); 39 } 40 if (cur.n * 2< N && f[cur.n * 2]==-1) 41 { 42 f[cur.n*2] = cur.step + 1; 43 q.push(qnode(cur.n*2, cur.step + 1)); 44 } 45 if (f[k]!=-1){ans=f[k]; return;} 46 } 47 } 48 49 int main() 50 { 51 #ifndef ONLINE_JUDGE 52 freopen("poj3278.in","r",stdin); 53 #endif 54 scanf("%d %d",&n,&k); 55 if (n<k) bfs(); 56 else ans = n-k; 57 58 printf("%d\n",ans); 59 return 0; 60 }
D-Fliptile (poj 3279)
翻动棋盘上的棋子使所有棋子白面朝上,且每次翻动会带动相邻的四颗棋子,问翻动方案
乍一看需要枚举的方案很多,但我们发现当某一行翻动方案确定时,下一行的方案也就确定了
比如某一行在下一行翻动以前状态为0 0 1 0 那么显然下一行就应该翻动第3个位置,以此类推,发现真正需要枚举的只有第一行的翻动方案
1 #include2 #include 3 4 const int N = 15+5; 5 const int INF = 0xfffffff; 6 7 int row[N]; 8 int maps[N][N]; 9 int opt[N][N],ansi; 10 int ans_opt[N][N]; 11 int r,c; 12 int ans=INF; 13 14 void reverse(int *a,int pos) 15 { 16 if(pos-1>=1) a[pos-1]^=1; 17 a[pos]^=1; 18 if(pos+1<=c) a[pos+1]^=1; 19 } 20 21 int main() 22 { 23 #ifndef ONLINE_JUDGE 24 freopen("poj3279.in","r",stdin); 25 #endif 26 27 scanf("%d %d",&r,&c); 28 29 for(int i=1;i<=r;i++) 30 for(int j=1;j<=c;j++) 31 scanf("%d",&maps[i][j]); 32 33 for(int i=0; i< (1< ) 34 { 35 //枚举第一行翻动情况 36 memset(opt,0,sizeof(opt)); 37 int cur_ans = 0; 38 int t=i,last_t=0; 39 40 for(int u=1;u<=r;u++) 41 { 42 int cnt=0; 43 for(int v=1;v<=c;v++) row[v]=maps[u][v]; 44 45 //DEBUG 46 //if(i==10) for(int p=1;p<=c;p++) printf("%d ",row[p]); 47 48 while(last_t){ 49 cnt++; 50 if (last_t&1) row[cnt] ^= 1; 51 last_t>>=1; 52 } 53 last_t = t; 54 55 cnt=0; 56 while(t){ 57 cnt++; 58 if (t&1) 59 { 60 opt[u][cnt] = 1; 61 cur_ans++; 62 reverse(row,cnt); 63 } 64 t>>=1; 65 } 66 if (cur_ans > ans) break; 67 //check 68 if(u==r) 69 { 70 int flag = 1; 71 for(int v=1;v<=c;v++) 72 if(row[v]) 73 { 74 flag=0; 75 break; 76 } 77 if(flag) 78 { 79 if(cur_ans<ans) 80 { 81 ans = cur_ans; 82 for(int p=1;p<=r;p++) 83 for(int q=1;q<=c;q++) 84 ans_opt[p][q]=opt[p][q]; 85 } 86 } 87 } 88 //由当前行推出下一行翻动状态 89 t = 0; 90 for(int v=c;v>=1;v--){ 91 t<<=1; 92 if (row[v]) t|=1; 93 } 94 } 95 } 96 97 if (ans!=INF) 98 for(int p=1;p<=r;p++) 99 { 100 for(int q=1;q<=c;q++) printf("%d ",ans_opt[p][q]); 101 printf("\n"); 102 } 103 else printf("IMPOSSIBLE\n"); 104 return 0; 105 }
E-Find The Multiple(poj 1426)
给定一个数字n,求其倍数m使得每一位只有0和1
第一想法居然是高精度...我真是傻到家了
这里的搜索有点意思,就是枚举第k位为0或者1时模n的结果,如果为零则为答案
比如k = xxxxxxx (x为0或1)
xxxxxxx1 % n= (10 * k + 1 )%n
xxxxxxx0 % n= (10*k)%n
因此我们可以把这些mod以后的结果保存下来,由小到大递推
mod_res[xxxxxxx1] = (mod_res[xxxxxxx] * 10 + 1)%n
mod_res[xxxxxxx0] = (mod_res[xxxxxxx] * 10)%n
即可找到答案
然后...200以内打表打法,就不贴了....(丢人