概述
搜索算法是利用计算机的高性能来有目的穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。它在不仅仅在算法和人工智能中占有很重要的地位,而且在图论和其他数学方面有很强的实际应用背景。另外,很多算法如动态规划、贪心等都是搜索算法的扩展,这是因为这些算法找到了某些规律来进行变相剪枝而已。搜索算法考虑这三个问题即可:1. 对于一个问题,怎样快速建立状态空间;2. 提出一个合理的搜索策略;3. 简单估计考虑剪枝的时空性能和费用。
一、Depth first search(深度优先搜索)
深度优先搜索(DFS搜索)实际上就是按照深度优先的顺讯来遍历状态空间,一般递归或栈来实现,如下图所示。换句话说就是犹如走迷宫,不撞南山不回头。
其大致算法描述如下:
void DFS(int state, int depth){ for(int i=1; i<=state; i++){ newstate = doOperand(state); if(answer =true) cout<< answer; else if(depth<maxdepth) DFS(newstate,depth); } }
接下来,看一些例子:
例1:N皇后问题
在这里,如果预先将结果存入数值就ac (代码1),否则就超时(代码2)。
1 #include <iostream> 2 #include <string> 3 //#include <memory.h> 4 5 using namespace std; 6 7 int q[15]; 8 9 bool Check(int k){ 10 11 for(int i=0; i<k; i++){ 12 if(q[k] == q[i] || q[k]-q[i] == k-i || q[k]-q[i] == i-k) 13 return false; 14 } 15 16 return true; 17 } 18 19 int DFS(int r, int n){ 20 21 if(r == n) 22 return 1; 23 24 int ans = 0; 25 26 for(q[r]=1; q[r]<=n; q[r]++){ 27 if(Check(r)) 28 ans += DFS(r+1, n); 29 } 30 31 return ans; 32 } 33 34 int main(){ 35 36 int n; 37 //memset(q, 0, sizeof(q)); 38 39 while(cin >> n && n){ 40 cout << DFS(0, n)<<endl; 41 } 42 43 return 0; 44 }
1 #include <iostream> 2 #include <string> 3 #include <memory.h> 4 5 using namespace std; 6 7 int q[15], res[15]; 8 9 bool Check(int k){ 10 11 for(int i=0; i<k; i++){ 12 if(q[k] == q[i] || q[k]-q[i] == k-i || q[k]-q[i] == i-k) 13 return false; 14 } 15 16 return true; 17 } 18 19 int DFS(int r, int n){ 20 21 if(r == n) 22 return 1; 23 24 int ans = 0; 25 26 for(q[r]=1; q[r]<=n; q[r]++){ 27 if(Check(r)) 28 ans += DFS(r+1, n); 29 } 30 31 return ans; 32 } 33 34 int main(){ 35 36 int n; 37 // 38 for(int i=1; i<=10; i++){ 39 memset(q, 0, sizeof(q)); 40 res[i] = DFS(0,i); 41 } 42 while(cin >> n && n){ 43 cout << res[n] <<endl; 44 } 45 46 return 0; 47 }
据说标准答案:
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 #include "algorithm" 6 using namespace std; 7 int Sum, Num, Son[15], Ans[15], Flag; 8 void DFS(int p, int k, int s) 9 { 10 int Last = -1; 11 if(s>Sum) return; 12 if(s==Sum) 13 { 14 Flag = 0; 15 for(int j=0; j<k-1; j++) cout<<Ans[j]<<"+"; 16 cout<<Ans[k-1]<<endl; 17 } 18 for(int i=p; i<Num; i++) 19 { 20 if(s+Son[i] > Sum) continue; 21 if(Son[i]!=Last) 22 { 23 Last=Ans[k]=Son[i]; 24 DFS(i+1, k+1, s+Son[i]); 25 } 26 } 27 } 28 int main() 29 { 30 while(cin>>Sum>>Num && (Sum+Num)) 31 { 32 Flag = 1; 33 for(int i=0; i<Num; i++) cin>>Son[i]; 34 cout<<"Sums of "<<Sum<<":"<<endl; 35 DFS(0, 0, 0); 36 if(Flag) cout<<"NONE"<<endl; 37 } 38 }
例4:Prime Ring Problem / Prime Ring Problem
在ZOJ1457就超时,在HUD1016就能AC。这里用递归法给出代码
1 #include <iostream> 2 #include <string> 3 #include <memory.h> 4 5 using namespace std; 6 7 int q[25], prime[45]; 8 9 void GetPrime(){ 10 bool flag; 11 prime[2] = 1; 12 for(int i=3; i<=40; i++){ 13 flag = true; 14 for(int j=2; j*j <= i; j++){ 15 if(i % j == 0){ 16 flag = false; 17 break; 18 } 19 } 20 if(flag) 21 prime[i] = 1; 22 } 23 } 24 25 26 bool Check(int k){ 27 if(!prime[q[k]+q[k-1]]) 28 return false; 29 30 for(int i=0; i<k; i++){ 31 if(q[k] == q[i]) 32 return false; 33 } 34 35 return true; 36 } 37 38 void DFS(int r, int n){ 39 40 if(r == n && prime[q[0]+q[n-1]]){ 41 for(int i=0; i<n-1; i++){ 42 cout << q[i] << " "; 43 } 44 cout << q[n-1] << endl; 45 } 46 47 for(q[r]=1; q[r]<=n; q[r]++){ 48 if(Check(r)) 49 DFS(r+1, n); 50 } 51 } 52 53 int main(){ 54 55 int n,k; 56 memset(prime, 0, sizeof(prime)); 57 GetPrime(); 58 59 k =1; 60 while(cin >> n){ 61 cout<<"Case "<<k<<":"<<endl; 62 memset(q, 0, sizeof(q)); 63 q[0] = 1; 64 DFS(1, n); 65 k++; 66 cout << endl; 67 } 68 69 return 0; 70 }
一、Breadth First Search(广度优先搜索)
广度优先搜索(BFS搜索)实际上就是按照广度优先的顺讯来遍历状态空间,一般队列来实现,如下图所示。换句话说就是先寻找最近的东西,然后去扩大范围去寻找远的东西。
一般步骤如下:
1. 从队列中取出一个结点,检查它按照扩展规则是否能够扩展,如果能则产生一个新结点。
2. 检查新生成的结点,看它是否已在队列中存在。如果存在,则放弃该结点,然后回到第一步。否则新结点加入到队列尾
3. 检查新结点是否目标结点。如果是,搜索成功,程序结束。若不是,则回到第一步。
最终产生两种结果,找到目标或无法找到目标。广度优先能保证找到一条通向它的最佳路径。
例1. Catch That Cow
1 #include <iostream> 2 #include <queue> 3 #include <string> 4 5 using namespace std; 6 #define MAX 100000 7 int step[100005], used[100005]; 8 queue<int> q; 9 10 void BFS(int n, int k){ 11 12 int head, x; 13 q.push(n); 14 used[n] = 1; 15 step[n] = 0; 16 while(!q.empty()){ 17 head = q.front(); 18 q.pop(); 19 x = head; 20 if(head == k) 21 cout<< step[head] <<endl; 22 if(x-1>0 && !used[x-1]){ 23 q.push(x-1); 24 used[x-1] = 1; 25 step[x-1] = step[x] + 1; 26 } 27 if(x+1 <= MAX && !used[x+1]){ 28 q.push(x+1); 29 used[x+1] = 1; 30 step[x+1] = step[x] + 1; 31 } 32 33 if(2*x <= MAX && !used[2*x]){ 34 q.push(2*x); 35 used[2*x] = 1; 36 step[2*x] = step[x] + 1; 37 } 38 } 39 } 40 41 42 int main(int argc, char *argv[]) 43 { 44 int n,k; 45 46 47 while(cin>>n>>k){ 48 memset(used, 0, sizeof(used)); 49 memset(step, 9, sizeof(step)); 50 if(n>=k) 51 cout << n-k <<endl; 52 else 53 BFS(n,k); 54 } 55 return 0; 56 }
例2. Jugs
刚开始蛮力搜索,结果Memory Limit Exceeded。代码如下:
1 #include <iostream> 2 #include <string> 3 #include <queue> 4 #include <stack> 5 6 using namespace std; 7 8 struct data{ 9 int a, b, site; 10 }; 11 struct way{ 12 int site, str, pre; 13 }; 14 stack<way> w; 15 string s[10] = {"fill A\n","fill B\n","empty A\n", "empty B\n","pour A B\n","pour B A\n", "success"}; 16 int k; 17 18 data GetJugs(data cur, int a, int b, int c){ 19 data next; 20 switch(c){ 21 case 0: next.a = a; next.b = cur.b; next.site = k++; break; 22 case 1: next.a = cur.a; next.b = b; next.site = k++; break; 23 case 2: next.a = 0; next.b = cur.b; next.site = k++; break; 24 case 3: next.a = cur.a; next.b = 0; next.site = k++; break; 25 case 4: next.a = (cur.a+cur.b<b)?0:(cur.a+cur.b-b); next.b = (cur.a+cur.b>b)?b:(cur.a+cur.b); next.site = k++; break; 26 case 5: next.a = (cur.a+cur.b>a)?a:(cur.a+cur.b); next.b = (cur.a+cur.b<a)?0:(cur.a+cur.b-a); next.site = k++; break; 27 } 28 return next; 29 } 30 31 void BFS(int a, int b, int target){ 32 queue<data> q; 33 data next, cur; 34 way wCur; 35 cur.a = a; cur.b = 0; cur.site = k++; 36 q.push(cur); 37 wCur.str = 0; wCur.site = cur.site; wCur.pre = 0; 38 w.push(wCur); 39 cur.a = 0; cur.b = b; cur.site = k++; 40 q.push(cur); 41 wCur.str = 1; wCur.site = cur.site; wCur.pre = 1; 42 w.push(wCur); 43 44 while(!q.empty()){ 45 cur = q.front(); 46 q.pop(); 47 48 if(cur.b == target){ 49 wCur.str = 6; wCur.pre = cur.site; wCur.site = -1; 50 w.push(wCur); 51 break; 52 } 53 for(int i=0; i<=5; i++){ 54 next = GetJugs(cur, a, b, i); 55 q.push(next); 56 wCur.str = i; wCur.site = next.site; wCur.pre = cur.site; 57 w.push(wCur); 58 } 59 } 60 } 61 62 63 int main(int argc, char *argv[]) 64 { 65 int a,b,t; 66 way next, cur; 67 string str; 68 while(cin>>a>>b>>t){ 69 k=0; 70 BFS(a,b,t); 71 if(!w.empty()){ 72 cur = w.top(); 73 w.pop(); 74 str = s[cur.str]; 75 } 76 while(!w.empty()){ 77 next = w.top(); 78 w.pop(); 79 if(next.site == cur.pre){ 80 str = s[next.str]+str; 81 cur = next; 82 } 83 } 84 cout <<str<<endl; 85 } 86 return 0; 87 }
由于有一些重复,需要去掉重复,因此加入jugs的数组来判断是否前面出现过,就AC了。代码如下
1 #include <iostream> 2 #include <string> 3 #include <queue> 4 #include <stack> 5 #include <cstring> 6 7 using namespace std; 8 9 struct data{ 10 int a, b, site; 11 }; 12 struct way{ 13 int site, str, pre; 14 }; 15 stack<way> w; 16 string s[10] = {"fill A\n","fill B\n","empty A\n", "empty B\n","pour A B\n","pour B A\n", "success"}; 17 int k, jug[1005][1005]; 18 19 data GetJugs(data cur, int a, int b, int c){ 20 data next; 21 switch(c){ 22 case 0: next.a = a; next.b = cur.b; next.site = k++; break; 23 case 1: next.a = cur.a; next.b = b; next.site = k++; break; 24 case 2: next.a = 0; next.b = cur.b; next.site = k++; break; 25 case 3: next.a = cur.a; next.b = 0; next.site = k++; break; 26 case 4: next.a = (cur.a+cur.b<b)?0:(cur.a+cur.b-b); next.b = (cur.a+cur.b>b)?b:(cur.a+cur.b); next.site = k++; break; 27 case 5: next.a = (cur.a+cur.b>a)?a:(cur.a+cur.b); next.b = (cur.a+cur.b<a)?0:(cur.a+cur.b-a); next.site = k++; break; 28 } 29 return next; 30 } 31 32 void BFS(int a, int b, int target){ 33 queue<data> q; 34 data next, cur; 35 way wCur; 36 cur.a = a; cur.b = 0; cur.site = k++;jug[cur.a][cur.b] = 1; 37 q.push(cur); 38 wCur.str = 0; wCur.site = cur.site; wCur.pre = 0; 39 w.push(wCur); 40 cur.a = 0; cur.b = b; cur.site = k++;jug[cur.a][cur.b] = 1; 41 q.push(cur); 42 wCur.str = 1; wCur.site = cur.site; wCur.pre = 1; 43 w.push(wCur); 44 45 while(!q.empty()){ 46 cur = q.front(); 47 q.pop(); 48 49 if(cur.b == target){ 50 wCur.str = 6; wCur.pre = cur.site; wCur.site = -1; 51 w.push(wCur); 52 break; 53 } 54 for(int i=0; i<=5; i++){ 55 next = GetJugs(cur, a, b, i); 56 if(jug[next.a][next.b]) continue; 57 jug[next.a][next.b] = 1; 58 q.push(next); 59 wCur.str = i; wCur.site = next.site; wCur.pre = cur.site; 60 w.push(wCur); 61 } 62 } 63 } 64 65 66 int main(int argc, char *argv[]) 67 { 68 int a,b,t; 69 way next, cur; 70 string str; 71 while(cin>>a>>b>>t){ 72 k=0; 73 BFS(a,b,t); 74 memset(jug, 0, sizeof(jug)); 75 if(!w.empty()){ 76 cur = w.top(); 77 w.pop(); 78 str = s[cur.str]; 79 } 80 while(!w.empty()){ 81 next = w.top(); 82 w.pop(); 83 if(next.site == cur.pre){ 84 str = s[next.str]+str; 85 cur = next; 86 } 87 } 88 cout <<str<<endl; 89 } 90 return 0; 91 }
还有zoj1091,1047,1103,1649,1310,1136,1530,1301