DFS 與 BFS 的定位
狀態表示與建圖
visited 何時標?
遞迴 vs 疊代
DFS 的三種「走法」順序(對樹/有根圖很實用)
時間戳與邊類型(進階)
原題:
https://cses.fi/problemset/task/1669
題意:
給一張無向圖(n 個點、m 條邊),請找出任意一個長度 ≥ 3 的環並輸出;若不存在則輸出 IMPOSSIBLE。
解題思路:
用 DFS 探索
當處理 u 的鄰居 v:
以 疊代版 DFS(顯式 stack) 實作,避免鏈狀圖遞迴棧溢位
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, m;
vector<int> g[MAXN];
int parentArr[MAXN];
char visited[MAXN]; //是否看過
char inStack[MAXN]; //是否在目前 DFS「路徑棧」上(用來判定回邊是否指向祖先)
bool find_cycle(vector<int>& cycle_out) {
    // 每個連通分量各跑一次
    vector<int> idx(n+1, 0); // 每個點鄰接表的掃描指標
    for (int s=1; s<=n; s++) {
        if (visited[s]) continue;
        stack<int> st;
        st.push(s);
        visited[s] = 1;
        inStack[s] = 1;
        parentArr[s] = 0;
        while (!st.empty()) {
            int u = st.top();
            // 如果這個點的鄰居都掃完,就彈出(結束 u 的遞迴)
            if (idx[u] >= (int)g[u].size()) {
                st.pop();
                inStack[u] = 0; // 離開當前遞迴路徑
                continue;
            }
            int v = g[u][ idx[u]++ ];
            if (v == parentArr[u]) continue; // 無向圖的反向邊
            if (!visited[v]) {
                visited[v] = 1;
                inStack[v] = 1;
                parentArr[v] = u;
                st.push(v);
            } else if (inStack[v]) {
                // 發現回邊 u -> v(v 是祖先)→ 重建一個環
                vector<int> cyc;
                cyc.push_back(v);
                int x = u;
                while (x != v) {
                    cyc.push_back(x);
                    x = parentArr[x];
                }
                cyc.push_back(v); // 收尾,首尾相同形成閉合環
                cycle_out.swap(cyc);
                return true;
            }
        }
    }
    return false;
}
int main() {
    cin >> n >> m;
    for (int i=1; i<=n; ++i) {
        g[i].clear();
        visited[i] = inStack[i] = 0;
        parentArr[i] = 0;
    }
    for (int i = 0; i < m; ++i) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    vector<int> cycle;
    if (!find_cycle(cycle)) {
        cout << "IMPOSSIBLE\n";
        return 0;
    }
    cout << (int)cycle.size() << "\n";
    for (int i=0; i < (int)cycle.size(); ++i) {
        if (i) cout << ' ';
        cout << cycle[i];
    }
    cout << "\n";
    return 0;
}
原題:
https://cses.fi/problemset/task/1668
題意:
給 n 點 m 無向邊,是否能把點分成兩隊,且每條邊兩端點在不同隊?若可,輸出每點所屬隊(1/2),否則 IMPOSSIBLE。
解題思路:
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<vector<int> > g(n + 1);
    for (int i = 0; i < m; ++i) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    vector<int> color(n + 1, 0); // 0=未著色, 1/2=兩隊
    for (int s = 1; s <= n; ++s) if (color[s] == 0) {
        stack<int> st;
        st.push(s);
        color[s] = 1;
        while (!st.empty()) {
            int u = st.top(); st.pop();
            for (size_t i = 0; i < g[u].size(); ++i) {
                int v = g[u][i];
                if (color[v] == 0) {
                    color[v] = 3 - color[u]; // 1<->2
                    st.push(v);
                } else if (color[v] == color[u]) {
                    cout << "IMPOSSIBLE\n";
                    return 0;
                }
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        if (i > 1) cout << ' ';
        cout << color[i];
    }
    cout << "\n";
    return 0;
}
原題:
https://leetcode.com/problems/number-of-islands/description/
題意:
'1' 表陸地、'0' 表水面,四向相鄰。求島嶼數量。
註:斜對角不算相連。
解題思路:
掃描網格,遇到 '1' 就啟動一趟 DFS,把整個連通陸地變為 '0'(或 visited),計數 +1。
class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int n = (int)grid.size();
        if (n == 0) return 0;
        int m = (int)grid[0].size();
        const int DX[4] = {-1, 1, 0, 0};
        const int DY[4] = {0, 0, -1, 1};
        int count = 0;
        for (int i=0; i<n; i++) {
            for (int j=0; j<m; j++) {
                if (grid[i][j] != '1') continue;
                count++;
                stack<pair<int,int>> st;
                st.push(make_pair(i, j));
                grid[i][j] = '0';
                while (!st.empty()) {
                    pair<int,int> cur = st.top(); st.pop();
                    int x = cur.first, y = cur.second;
                    for (int k=0; k<4; k++) {
                        int nx = x+DX[k], ny = y+DY[k];
                        if (nx<0 || nx>=n || ny<0 || ny>=m) continue;
                        if (grid[nx][ny] != '1') continue;
                        grid[nx][ny] = '0';
                        st.push(make_pair(nx, ny));
                    }
                }
            }
        }
        return count;
    }
};
原題:
https://leetcode.com/problems/max-area-of-island/description/
題意:
同上,但要求回傳最大的連通陸地面積。
解題思路:
掃描網格,遇到 '1' 開 DFS 擴張累加面積,並把訪過的變 '0';取所有連通塊中的最大值。
class Solution {
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int n = (int)grid.size();
        if (n == 0) return 0;
        int m = (int)grid[0].size();
        const int DX[4] = {-1, 1, 0, 0};
        const int DY[4] = {0, 0, -1, 1};
        int best = 0;
        for (int i=0; i<n; i++) {
            for (int j=0; j<m; j++) {
                if (grid[i][j] != 1) continue;
                int area = 0;
                stack<pair<int,int>> st;
                st.push(make_pair(i, j));
                grid[i][j] = 0;
                while (!st.empty()) {
                    pair<int,int> cur = st.top(); st.pop();
                    int x = cur.first, y = cur.second;
                    area++;
                    for (int k = 0; k < 4; ++k) {
                        int nx = x+DX[k], ny = y+DY[k];
                        if (nx<0 || nx>=n || ny<0 || ny>=m) continue;
                        if (grid[nx][ny] != 1) continue;
                        grid[nx][ny] = 0;
                        st.push(make_pair(nx, ny));
                    }
                }
                if (area>best) best=area;
            }
        }
        return best;
    }
};