窮竭搜索

窮極搜索主要包括兩個方面:
1.深度優先搜索
2.廣度優先搜索

一些基本的思想
1.遞歸函數
在函數中調用自己的函數就是遞歸函數,例如階乘函數可以定義爲:
int fact(int n)
{
if (n == 0) return 1;
return n * fact(n - 1);
}
遞歸最重要的一點就是函數的停止條件,在上面的例子中,當傳入的參數爲0時,就停止遞歸。
假如一個函數遞歸沒有終結條件,那麼將會反覆調用自身,直到棧溢出。

斐波那契數列的例子如下:
int fib(int n)
{
if ( n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
注意到,在這個遞歸調用時, x一些情況被重複計算了,所以如果能有一張表記錄以前的計算結果,那麼再次運行時就不需要重新計算,將會省去大量時間。
改寫後的程序如下:
int memo[MAX_N+1];

int fib(int n)
{
if ( n <=1 ) return n;
if(memo[n] != 0) return memo[n];
return memo[n] = fib(n - 1) + fib(n - 2);
}

2.棧
先進後出的數據結構,可以使用C++標準庫中的stack實現
一些操作的例子如下:
stack<int> s;
s.push(1);
s.top();
s.pop();

3.隊列
先進先出的數據結構,可以使用C++標準庫中的queue實現
一些操作的例子如下:
queue<int> que;
que.push(1);
que.front();
que.pop();

深度優先搜索(DFS)
深度優先主要通過狀態的轉移不斷的尋找下一個狀態,知道不能夠繼續轉移了,則向前回溯一個狀態,查看有沒有其他狀態可以轉移,知道搜索完所有狀態。

例子:部分和問題

題目要求:給定整數a1,a2...an,判斷是否可以選擇若干個數,使其和恰好爲k
例如:
輸入  n=4 a={1,2,4,7} k=13
輸出  yes(13 = 2 + 4 + 7)
輸入  n=4 a={1,2,4,7} k=15
輸出  no

分析:這裏最直接的方法就是通過試試所有的方案看看加起來有沒有等於k的情況,但難點就在於怎麼去枚舉所有的情況,這裏需要一些小技巧:我們發現,對於每一個ai,只有兩種選擇,加上或者不加上,那麼就可以將所有的輸入組織成一棵二叉樹,每一個節點代表當前的數字現在所有的和是多少,左邊節點代表不加上當前的數字,右邊節點代表加上這個數字。這樣,只要遍歷整棵樹,就可以瞭解所有情況。
代碼如下:
int a[MAX_N];
int n, k;

/*計算第i個數的時候此時總和爲sum,此時就有兩種選擇,加上i或者不加i,分別使用兩次遞歸*/
bool dfs(int i,int sum)
{
if(i == n) return sum = k;
if(dfs(i + 1, sum)) return true;
if(dfs(i + 1, sum + a[i])) return true;
return false;
}
void solve()
{
if(dfs(0, 0)) printf("yes");
else printf("no");
}

廣度優先搜索(BFS)
廣度優先總是搜索距離初始狀態近的狀態,並由近至遠依次搜索其他的狀態,直到搜索完畢
在數據結構上,使用隊列可以很方便的完成廣度搜索的方法,首先將距離初始狀態最近的狀態如隊列,然後從隊列中不斷的取出狀態,然後把取出的狀態可以轉移到的其他狀態如隊列,如此往復,直到隊列爲空。

例子:迷宮的最短路徑

題目要求:給定一個大小爲N*M的迷宮,迷宮由通道和牆壁組成,每一步可以向鄰接的上下左右4格作出移動,求從起點到終點移動的最少步數。
例如:
輸入:  #S######.#
   ......#..#
       .#.##.##.#
       .#........
       ##.##.####
   ....#....#
   .#######.#
   ....#.....
   .####.###.
   ....#...G#
輸出:22
分析:無論深度優先還是廣度優先,都涉及到了狀態的轉移,在迷宮問題中,每個狀態就是所在位置的座標,所以使用pair來表示狀態。轉移的方向爲4個方向。在搜索的過程中,只需要將訪問的距離

代碼如下:
const int INF = 1000000;

typedef pair<int, int> P;                              //迷宮的座標使用pair表示

char maze[MAX_N][MAX_M+1];                //表示迷宮的字符串數組
int N, M;
int sx, sy;                                                        //起點座標
int gx, gy;                                                       //終點座標

int d[MAX_N][MAX_M];                               //到各個位置距離最短的數組

/*四個方向的表示,注意這裏使用了兩個數組,這樣在後面可以使用一個循環就簡單的訪問四個方向*/
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};

int bfs()
{
queue<P> que;
/*所有位置初始化爲INF*/
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++) d[i][j] = INF;
/*將起點入隊列,並將距離設置爲0*/
que.push(P(sx, sy));
d[sx][sy] = 0;

while (que.size())
{
/*從隊列最前端取出元素*/
P p = que.front(); que.pop();
/*如果已經是終點,停止搜索*/
if (p.first == gx && p.second == gy) break;

/*四個方向的循環*/
for (int i = 0; i < 4; i++)
{
/*新的座標位置*/
int nx = p.first + dx[i], ny = p.second + dy[i];

/*判斷是否可以移動或者或者已經訪問過了*/
if (0 <= nx && nx < N &&0 <=ny &&ny < M && maze[nx][ny] != '#' && d[nx][ny] == INF)
{
/*可以移動的話將其入隊列,並將該位置確定爲p位置+1*/
que.push(P(nx, ny));
d[nx][ny] = d[p.first][p.second] + 1;
}
}
}
return d[gx][gy];
}
void solve()
{
int res = bfs();
printf("%d\n", res);
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章