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;
}
};