### [^1] [P3邏輯電路](https://zerojudge.tw/ShowProblem?problemid=m933) 題目看似複雜,但應該不難想到使用[圖(Graph)](https://web.ntnu.edu.tw/~algo/Graph.html)相關的演算法處理,不外乎兩種:[DFS](https://yuihuang.com/dfs-depth-first-search/)/BFS。 我們把所有的輸入端口、邏輯閘、輸出端口都看成是節點,以此建立一個有向圖,題目要求是終端的訊號,從**終端開始往前推**會比從端口出發來的有效率,因此把每個終端當做**根節點**(Root),作DFS一直遍歷到葉節點,就能求出終端的訊號。 以下程式碼為資料的輸入,圖的儲存方式使用Adjacency Lists。 若還沒學過[map](https://www.geeksforgeeks.org/map-associative-containers-the-c-standard-template-library-stl/)可以用二維陣列替代。 ```=C++ int p, q, r, m; //v[i] 表示節點i的輸出訊號,-1為無訊號 vector<int> v(60005, -1); int t[50005]; //邏輯閘 //mp[i]表示i的訊號來源,最多為2個:mp[i][0], mp[i][1] map<int, vector<int>> mp; cin >> p >> q >> r >> m; for(int i=1;i<=p;i++) cin >> v[i]; for(int i=1;i<=q;i++) cin >> t[i]; for(int i=0;i<m;i++){ //start連到end,end的訊號來源加上start int start, end; cin >> start >> end; mp[end].push_back(start); } ``` 接者開始DFS,進入節點時,我們會遇到三種狀況:節點為輸入端口、邏輯閘、輸出端口 ```=C++ void dfs(int st){ if 輸入 return else if 邏輯閘 dfs(子節點) else 輸出 dfs(子節點) } ``` 新增一個一維陣列 mx[i],表示以i為根節點的最大延遲時間-> ```=C++ vector<int> mx(60005, -1); void dfs(int st){ if 輸入端口 mx[st] = 0; return; else if 邏輯閘 dfs(子節點); mx[st] = 1 + max(mx[子節點]) else 輸出端口 dfs(子節點); mx[st] = mx[子節點]; } ``` 同時計算訊號v[i] ```=C++ void dfs(int st){ if 輸入端口 else if 邏輯閘 dfs(子節點); //not v[st] = !v[子節點]; //and v[st] = v[子節點1] && v[子節點2]; //or v[st] = v[子節點1] || v[子節點2]; //xor:若不同則為正邏輯=>相同為正邏輯再顛倒 v[st] = !(v[子節點1] == v[子節點2]); else 輸出端口 dfs(子節點); v[st] = v[子節點]; } ``` 要注意這裡無論求訊號或延遲時間,**必須先DFS再計算**(先DFS才能先求出子節點的資料) :::spoiler #### 完整CODE: ```=C++ #include <bits/stdc++.h> using namespace std; int p, q, r, m; vector<int> v(60005, -1); vector<int> mx(60005, -1); int t[50005]; map<int, vector<int>> mp; ``` ```=C++ void dfs(int st){ if(st <= p) { mx[st] = 0; return; } if(mx[st] != -1) return; else if(st >= p+1 && st <= p+q){ if(t[st-p] == 4){ //not只有一個子節點 dfs(mp[st][0]); v[st] = !v[mp[st][0]]; mx[st] = 1 + mx[mp[st][0]]; }else{ int p1 = mp[st][0]; int p2 = mp[st][1]; dfs(p1); dfs(p2); if(t[st-p] == 1){ v[st] = v[p1] && v[p2]; } if(t[st-p] == 2){ v[st] = v[p1] || v[p2]; } if(t[st-p] == 3){ v[st] = !(v[p1]==v[p2]); } mx[st] = 1 + max(mx[p1], mx[p2]); } }else{ dfs(mp[st][0]); v[st] = v[mp[st][0]]; mx[st] = mx[mp[st][0]]; } } ``` ios::[^2] ```=C++ int main() { ios::sync_with_stdio(0); cin.tie(0); cin >> p >> q >> r >> m; for(int i=1;i<=p;i++){ cin >> v[i]; } for(int i=1;i<=q;i++){ cin >> t[i]; } for(int i=0;i<m;i++){ //start連到end=>end的訊號來源有start int start, end; cin >> start >> end; mp[end].push_back(start); } int ans = 0; for(int i=p+q+1;i<=p+q+r;i++){ dfs(i); ans = max(ans, mx[i]); } cout << ans << endl; for(int i=p+q+1;i<=p+q+r;i++){ cout << v[i] << " "; } return 0; } ``` ::: [^1]:第三題和第四題通常會進入到演算法部分,不再像第一二題只考驗語法,有時候程式效率太差(過多的重複計算或效率不佳的演算法)也會導致該題只有拿到部分分,觀察資料上限大小判斷解題方法也是技巧之一。 [^2]:ios::sync_with_stdio(0); cin.tie(0); 此段程式旨在提高資料輸入的效率,通常在輸入大量資料時可考慮使用。若為輸出大量資料,可加上"cout.tie(0);"