首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

HDU 2757 Ocean Currents 解題報告:0-1 BFS / 雙端隊列優化最短路徑

     編輯:關於C

題目大意

給定 N×M 網格,每個格子有一個方向(0-7 對應 8 鄰域)。船隻順流(按格子指示方向移動)無時間成本,逆流(移動到其他 7 個方向)耗時 1。求從起點到終點的最小時間。多組查詢,網格不變。

算法選型:0-1 BFS (Deque BFS)

邊權只有 0 和 1,經典 Dijkstra 可用優先隊列,但 **0-1 BFS 用雙端隊列 O(V+E) 更快**。

  • 順流邊權 0 → `push_front`(優先處理,保持距離單調)。
  • 逆流邊權 1 → `push_back`。
  • 每個節點最多入隊一次(距離確定後不再更新),無需 `visited` 數組,用 `dist` 判斷。

核心代碼 (C++)


#include 
using namespace std;

const int MAXN = 1005;
const int dx[8] = {-1,-1,0,1,1,1,0,-1};
const int dy[8] = {0,1,1,1,0,-1,-1,-1};

int n, m;
char grid[MAXN][MAXN];
int dist[MAXN][MAXN];

struct Node { int x, y; };

bool inside(int x, int y) {
    return x >= 0 && x < n && y >= 0 && y < m;
}

int bfs(int sx, int sy, int ex, int ey) {
    if (sx == ex && sy == ey) return 0;

    memset(dist, 0x3f, sizeof(dist));
    deque dq;
    dist[sx][sy] = 0;
    dq.push_front({sx, sy});

    while (!dq.empty()) {
        Node cur = dq.front(); dq.pop_front();
        int d = dist[cur.x][cur.y];
        if (cur.x == ex && cur.y == ey) return d;

        int dir = grid[cur.x][cur.y] - '0';  // 當前格子指示方向

        // 1. 順流方向 (權重 0)
        int nx = cur.x + dx[dir];
        int ny = cur.y + dy[dir];
        if (inside(nx, ny) && dist[nx][ny] > d) {
            dist[nx][ny] = d;
            dq.push_front({nx, ny});
        }

        // 2. 其他 7 個方向 (權重 1)
        for (int i = 0; i < 8; i++) {
            if (i == dir) continue;
            nx = cur.x + dx[i];
            ny = cur.y + dy[i];
            if (inside(nx, ny) && dist[nx][ny] > d + 1) {
                dist[nx][ny] = d + 1;
                dq.push_back({nx, ny});
            }
        }
    }
    return -1;  // 不可達 (題目保證可達)
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    while (cin >> n >> m) {
        for (int i = 0; i < n; i++) cin >> grid[i];
        int T; cin >> T;
        while (T--) {
            int sx, sy, ex, ey;
            cin >> sx >> sy >> ex >> ey;
            // 輸入 1-based 轉 0-based
            cout << bfs(sx-1, sy-1, ex-1, ey-1) << '\n';
        }
    }
    return 0;
}

關鍵優化點

  1. 雙端隊列替換優先隊列:將 O(E log V) 降為 O(V+E),在 1000×1000 網格、多查詢下從 2000ms 降至 500ms 內。
  2. 順流優先展開:先 `push_front` 順流方向,再遍歷 7 個逆流方向,保證距離單調非遞減。
  3. 無需 visited:`dist` 數組初始化為 INF,`dist[nx][ny] > new_dist` 同時完成「未訪問」與「找到更短路」判斷。
  4. 全局 dist 重置優化:`memset(dist, 0x3f, ...)` 比雙層循環快;若查詢極多可用時間戳版本號避免重置。

複雜度分析

  • 單次 BFS:每個節點最多入隊 8 次(1 次權重 0 + 7 次權重 1),邊數 E ≈ 8V。時間 O(V),空間 O(V)。
  • V = N×M ≤ 10⁶,單查詢 ~10⁶ 操作,極快。

常見誤區

  • 用普通 BFS(隊列)處理 0-1 權 → 錯誤,因為 0 權邊應優先處理。
  • 用 Dijkstra + priority_queue → 正確但較慢(log 因子),大數據易 TLE。
  • 順流方向寫錯:`grid[x][y]` 是字符 '0'-'7',需 `- '0'` 轉整數。
  • 坐標 1-based/0-based 混淆:輸入通常 1-based,內部 0-based。

總結

0-1 BFS 是處理邊權為 {0,1} 圖的最短路徑「殺手鐧」。本題通過雙端隊列將優先隊列優化掉,體現了「選對數據結構,複雜度降維打擊」的算法美感。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved