# 802. Find Eventual Safe States
## 想法
若從 u 出發不會走到環上,則 u 是 safe node
證明:
從 u 出發不會走到環上 -> 每條從 u 為起點的路,一定是有限長度的,並且會走到 terminal node -> u 是 safe node
## 演算法(DFS)
```=
dfs(adj, u, visited, inCycle):
visited[u] = 1
for every u's neighbor v:
if visited[v] == 0:
inCycle[u] = inCycle[u] or dfs(adj, v, visited, inCycle)
else if visited[v] == 1:
inCycle[u] = 1
return 1
else:
inCycle[u] = inCycle[u] or inCycle[v]
visited[u] = 2
return inCycle[u]
main():
建構圖
宣告所需變數
for every u in V:
if !visited[u]:
dfs(adj, u, visited, inCycle)
ans = []
將所有inCycle[u] == 0的u加入ans
回傳ans
```
## 程式碼(DFS)
```cpp=
class Solution {
public:
bool dfs(vector<vector<int>> &adj, int u, vector<int> &color, vector<bool> &inCycle) {
color[u] = 1;
for (auto it = adj[u].begin(); it != adj[u].end(); it++) {
int v = *it;
if (color[v] == 0) {
inCycle[u] = inCycle[u] or dfs(adj, v, color, inCycle);
} else if (color[v] == 1) {
inCycle[u] = 1;
return 1;
} else {
inCycle[u] = inCycle[u] or inCycle[v];
}
}
color[u] = 2;
return inCycle[u];
}
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
int n = graph.size();
vector<vector<int>> adj(n, vector<int>());
for (int u = 0; u < n; u++) {
for (int v : graph[u]) {
adj[u].push_back(v);
}
}
vector<int> color(n);
vector<bool> inCycle(n);
for (int u = 0; u < n; u++) {
if (!color[u]) {
dfs(adj, u, color, inCycle);
}
}
vector<int> ans;
for (int u = 0; u < n; u++) {
if (!inCycle[u]) {
ans.push_back(u);
}
}
return ans;
}
};
```
## 演算法(BFS)
```=
建構圖 G=(V,E)
建構反向圖 G'=(V,E')
對 G' 做拓樸排序 將過程中曾經進入 queue 的點放到 ans,ans 即為答案
```
### 正確性與完備性證明
#### 正確性
定理:若 u 曾經進入 queue 則 u 是 safe node
證明:使用循環不變式
- ```初始```:拓樸排序一開始將 indeg 為 0 的點推入 queue,因為是反向圖,所以在原圖中是 outdeg 為 0 的點,故它是 safe node。
- ```維持```:過程中每拿出一個點 u,對於 u 的所有鄰居 v,移除 (u,v) 邊,若 indeg[v] == 0,則 v 會被推入 queue,根據歸納假設, u 是 safe node,如果移除 (u,v) 會使得 v 的 indeg 變為 0,說明在原圖中 v 只會抵達 safe node,根據定義,v 也是 safe node。
- ```終止```:當 queue 為空時,根據歸納假設,已經找到所有曾經進入 queue 的點,故 ans 裡的值都是 safe node。
#### 完備性
定理:若 u 是 safe node,則 u 會進入 queue
證明:若 u 是 safe node,那 u 可達 terminal node,並且 u 到 terminal 必存在一條最長路徑。我們聲明,對於每個 u ,u 到 terminal node 最長路徑為 k,則 u 一定會進入 queue。
- 使用歸納法,在初始情況,我們不會漏掉任何最長 0 步到 terminal node 的節點,也就是說他們都會進入 queue,成立;假設最長 k - 1 步到 terminal node 的節點都成立,即最長 k - 1 步到 terminal node 的節點都會進入 queue;則對於最長 k 步到 terminal node 的節點 v,因為 v 到 terminal node 最長只需要 k 步,在演算法移除所有最長 k - 1 步以內到 terminal node 的節點的所有出邊後,indeg[v] == 0,故 v 也會被推入 queue,成立;故根據歸納法,若 u 是 safe node,則 u 會進入 queue,證明完成。
## 程式碼(BFS)
```cpp=
class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
int n = graph.size();
vector<vector<int>> adj(n);
vector<int> indeg(n);
for (int u = 0; u < n; u++) {
for (int &v : graph[u]) {
adj[v].push_back(u);
indeg[u]++;
}
}
queue<int> q;
for (int i = 0; i < n; i++) {
if (!indeg[i]) {
q.push(i);
}
}
vector<int> ans;
while (!q.empty()) {
int u = q.front();
q.pop();
ans.push_back(u);
for (int &v : adj[u]) {
if (--indeg[v] == 0) {
q.push(v);
}
}
}
sort(begin(ans), end(ans));
return ans;
}
};
```