1. 程式人生 > >優先佇列和堆 實戰:HDU1242

優先佇列和堆 實戰:HDU1242

什麼叫優先佇列呢,能完成以下任務的就叫做優先佇列:

·插入一個數值

·取出最小的數值(獲取數值,並且刪除)

實現優先佇列,應該使用二叉樹完成,是一種叫二叉堆的資料結構(binary heap)

二叉堆分為兩種,最小堆和最大堆。最小堆是父節點的鍵值總是小於等於子節點的鍵值。最大堆是父節點的鍵值總是大於等於子節點的鍵值。

可以將二叉堆看成陣列的形式。

程式碼:

// 模擬最小堆
// 最小堆是二叉堆的一種,其特點是父節點的鍵值總是小於或者等於子節點。
// 實現細節:
//          push:向堆中插入資料時,首先在堆的末尾插入資料,然後不斷向上提升,直到沒有大小顛倒時。
//          pop:從堆中刪除最小值時首先把最後一個值複製到根節點上,並且刪除最後一個數值。然後不斷向下交換
//               直到沒有大小顛倒為止。在向下交換過程中,如果有兩個子兒子都小於自己,就選擇較小的
#include <iostream>
using namespace std;
const int MAX_N = 1005;
int heap[MAX_N], sz = 0;
void push(int x);
void display();
int pop();
int main()
{
    // 測試
    int x;
    int cmd;
    do
    {
        cout << "請輸入命令:1.push\t2.pop\t3.display\t0.quit\n";
        cin >> cmd;
        switch(cmd)
        {
        case 1:
            cout << "Input X:";
            cin >> x;
            push(x);
            break;
        case 2:
            x = pop();
            cout << x << "已取出!\n";
            break;
        case 3:
            display();
            break;
        }
    }while(cmd);

    return 0;
}

void push(int x)
{
    // i是要插入節點的下標
    int i = sz++;
    while(i > 0)
    {
        // p為父親節點的下標
        int p = (i - 1) / 2;
        // 如果父親節點小於等於插入的值,則說明大小沒有跌倒,可以退出
        if(heap[p] <= x)
            break;
        // 互換當前父親節點與要插入的值
        heap[i] = heap[p];
        i = p;
    }

    heap[i] = x;
    cout << "資料插入成功!\n";
}

int pop()
{
    // 取出根節點
    int ret = heap[0];
    // 將最後一個節點的值提到根節點上
    int x = heap[--sz];
    int i = 0;
    while(i * 2 + 1 < sz)
    {
        // a,b為左右兩個子節點的下標
        int a = 2 * i + 1, b = 2 * i + 2;
        // 去兩個子節點中較小的值
        if(b < sz && heap[b] < heap[a])
            a = b;
        // 如果已經沒有大小顛倒的話則退出迴圈
        if(heap[a] >= x)
            break;
        // 將父親節點與子節點互換
        heap[i] = heap[a];
        i = a;
    }
    heap[i] = x;

    return ret;
}

void display()
{
    for(int i = 0; i < sz; i++)
        cout << heap[i] << "\t";
    cout << endl;
}

但是在c++的STL中已經包含了優先佇列的高效實現——priority_queue,不過與上面的例子不一樣的是每次取出數值都是最大值。

看一個簡單的例項:

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	// 宣告 
	priority_queue<int> pque;
	
	// 插入元素 
	pque.push(3);
	pque.push(5);
	pque.push(1);
	
	// 不斷迴圈直到為空為止
	while(!pque.empty())
	{
		// 獲得最大值並且刪除 
		cout << pque.top() << endl;
		pque.pop();
	} 
	
	return 0;
} 

用杭電的一題來實戰一下吧

題目的意思是和走迷宮找最短出口差不多,就是有一個天使(在地圖上顯示為a),你是天使的好基友(在地圖上顯示為r),為了拯救基友,你打算去劫獄,.代表路, #代表牆壁。每走一步就要花一個時間單位。監獄裡當然會有很多獄警(在地圖上顯示為x)啦,但是基情使你充滿力量,輕輕鬆鬆能殺死一個獄警,也只要一個時間單位好了。現在你要怎麼做才能在最短的時間內救出好基友。

=_=

這題很明顯就是使用BFS來做的。因為裡面多了一個獄警,所以用佇列的話就比較麻煩了,所以在這裡使用優先佇列。

因為優先佇列預設是取出最大值的,所以首先要過載一下運算子,讓優先取出最小值。

ac程式碼如下:

#include <iostream>
#include <queue>
#include <string>
using namespace std;

const string fail = "Poor ANGEL has to stay in the prison all his life.";
const int INF = 1000005;
const int MAX_N = 205;
int N, M;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
char pic[MAX_N][MAX_N];
struct Node
{
    int x, y;
    int num;
    // 將優先佇列每次取出最大值改為每次去最小值
    bool operator < (const Node &a) const
    {
        return num > a.num;
    }
}first, next;
int bfs();

int main()
{
    while(cin >> N >> M)
    {
        int i, j;
        for(i = 0; i < N; i++)
        {
            cin >> pic[i];
            for(j = 0; j < M; j++)
            {
                if(pic[i][j] == 'a')
                {
                    first.x = i;
                    first.y = j;
                    first.num = 0;
                    pic[i][j] = '#';
                }
            }
        }
        int ans = bfs();
        if(ans == INF)
            cout << fail << endl;
        else
            cout << ans << endl;
    }

    return 0;
}

int bfs()
{
    priority_queue<Node> que;
    que.push(first);
    while(que.size())
    {
        first = que.top();
        que.pop();
        for(int i = 0; i < 4; i++ )
        {
            next.x = first.x + dx[i];
            next.y = first.y + dy[i];
            if(next.x >= 0 && next.x < N && next.y >=0 && next.y < M && pic[next.x][next.y] != '#')
            {
                next.num = first.num;
                if(pic[next.x][next.y] == 'r')
                    return next.num + 1;
                else if(pic[next.x][next.y] == '.')
                    next.num += 1;
                else if(pic[next.x][next.y] == 'x')
                    next.num += 2;
                pic[next.x][next.y] = '#';
                que.push(next);
            }
        }
    }

    return INF;
}