Search(搜索法)

概述

搜索算法是利用计算机的高性能来有目的穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。它在不仅仅在算法和人工智能中占有很重要的地位,而且在图论和其他数学方面有很强的实际应用背景。另外,很多算法如动态规划、贪心等都是搜索算法的扩展,这是因为这些算法找到了某些规律来进行变相剪枝而已。搜索算法考虑这三个问题即可: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

 

 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 }
代码2

 

例2:ZOJ1711:Sum it up

据说标准答案:

 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 }
View Code

例3:ZOJ1104:Anagrams by Stack

 

例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 }
View Code

 


 

 一、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 }
View Code

 

例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 }
View Code

由于有一些重复,需要去掉重复,因此加入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 }
View Code

 

还有zoj1091,1047,1103,1649,1310,1136,1530,1301

转载于:https://www.cnblogs.com/linspirit/articles/3840035.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值