【ACM算法回顧】簡單搜索


  • 今天主要回顧一下幾個搜索
    • 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 #include<cstdio>
2 #include<algorithm>
3 #include<cstring>
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 #include<cstdio>
8 #include<cstring>
9 #include<queue>
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 <qnode> 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 #include<cstdio>
2 #include<queue>
3 #include<cstring>
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 <qnode> 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 #include<cstdio>
2 #include<cstring>
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<<c); i++)
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以內打表打法,就不貼了....(丟人

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com