CCF201709-4通訊網路_雙向DFS
阿新 • • 發佈:2019-01-03
這題一看, 如果不考慮時間的話, 用floyd演算法求傳遞閉包和對稱閉包是可以解決的, 但是題目的資料只夠過60分, 正解顯然不是這樣.
然後我又思考, 開始往復雜了想, 於是有了下面的不知對錯的思路:
先判斷圖是否連通,否則結果為0
用tarjan演算法縮點, 把環全都合成一個結點,他們的處境是一樣的
找到一個入度為0的結點,(縮點之後一定存在), 它會指向好幾個點, 找出它指向點的共同點, 不斷找直到共同指向
一個點. 尋找過程中每個唯一的聚合點就是結果點
不僅實現起來難, 而且思路是否正確還存疑. 畢竟我還沒有掌握強連通分量縮點的演算法.
然後投降了, 找找部落格吧, 找到的第一篇部落格居然就用了縮點的演算法! 然後看其他部落格, 發現他們的共同點都是用了DFS.
這裡暴露出我的一個問題, 我的潛意識裡面認為DFS就是遞迴嘛, 遞迴都是很慢的嘛, 所以直接用DFS很容易超時的嘛. 然而並不! 所以遇到問題還是應該具體分析下時間, 不要被自己的潛意識誤導, 從而錯過正確的思路.
- 下面是DFS思路, 準確來說是雙向DFS
-
- 在有向圖中, 對於一個點, 它的DFS遍歷可達的點是它可傳遞資訊的點.
-
- 它的反向DFS遍歷(邊的方向反轉)可達的點是可傳遞資訊到它的點.
-
- 那麼我們獲取這兩種點, 如果這兩種點集包含除自己以外的所有點, 那麼答案+1.
- 那麼我們獲取這兩種點, 如果這兩種點集包含除自己以外的所有點, 那麼答案+1.
就是這麼簡單的思路, 雙向DFS, 找到可傳遞資訊的所有點. 需要注意的是, 這裡不應該在遍歷的過程中找到點就cnt++, 因為前後遍歷可能會出現重複的點(雙向), 就會出錯. 正確方法應該是記錄這些可達的點, 然後用hash的方法去統計.
最後此題學到的一個小經驗就是存圖的時候還是儘量用鄰接表吧, 用起來也方便, 而且不容易超時! 不容易超時! 不容易超時!
100分程式碼
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int maxn = 1005;
vector<int> Gp[maxn], Gs[maxn], to[maxn];
int n, m, vis[maxn], ans = 0;
void DFS(vector<int> G[maxn], int u, int scr)
{
for (int i = 0; i < G[u].size(); ++i) {
if (!vis[G[u][i]]) {
vis[G[u][i]] = true;
DFS(G, G[u][i], scr);
}
}
to[scr].push_back(u);
}
int main()
{
cin >> n >> m;
for (int i = 0, u, v; i < m; ++i) {
cin >> u >> v;
Gp[u - 1].push_back(v - 1);
Gs[v - 1].push_back(u - 1);
}
for (int i = 0; i < n; ++i) {
memset(vis, 0, sizeof(vis));
DFS(Gp, i, i);
}
for (int i = 0; i < n; ++i) {
memset(vis, 0, sizeof(vis));
DFS(Gs, i, i);
}
for (int i = 0; i < n; ++i) {
memset(vis, 0, sizeof(vis));
for (int j = 0; j < to[i].size(); ++j) {
vis[to[i][j]] = 1;
}
bool flag = false;
for (int j = 0; j < n; ++j) {
if (vis[j] == 0) flag = true;
}
if (flag == false) ans++;
}
cout << ans;
}