# 2025 University/College IC Design Contest Cell-Based IC Design Category for Graduate Level ## 題目就是要動態維護凸包 依據題目轉成C++的形式可以這樣寫,編譯完之後就用stdio吃pattern就可以了 `a.out < pattern1.dat` ```c #include <bits/stdc++.h> using namespace std; void run(vector<array<int, 2>> &points) { vector<int> nxt_idx(points.size(), -1); nxt_idx[1] = 2; nxt_idx[0] = 1; nxt_idx[2] = 0; int dx0 = points[1][0] - points[0][0], dx1 = points[2][0] - points[1][0], dy0 = points[1][1] - points[0][1], dy1 = points[2][1] - points[1][1]; bool circle_sign = (dx0 * dy1 - dx1 * dy0) < 0; int start_idx = 0, prev_idx = 0, cur_idx = 0, next_idx = 0; for (int i = 3; i < points.size(); i++) { cur_idx = start_idx; next_idx = nxt_idx[cur_idx]; dx0 = points[next_idx][0] - points[cur_idx][0]; dy0 = points[next_idx][1] - points[cur_idx][1]; dx1 = points[i][0] - points[cur_idx][0]; dy1 = points[i][1] - points[cur_idx][1]; int cp = dx0 * dy1 - dx1 * dy0; bool circle_sign_cur = cp < 0; bool prev_difside = 0, prev_zero = 0; bool cur_difside = circle_sign_cur != circle_sign, cur_zero = cp == 0; if (cur_zero) cur_difside = 0; // cout << "first difside " << cur_difside << " first sign " << cur_zero << endl; for (;;) { prev_idx = cur_idx; cur_idx = next_idx; next_idx = nxt_idx[next_idx]; prev_difside = cur_difside; prev_zero = cur_zero; dx0 = points[next_idx][0] - points[cur_idx][0]; dy0 = points[next_idx][1] - points[cur_idx][1]; dx1 = points[i][0] - points[cur_idx][0]; dy1 = points[i][1] - points[cur_idx][1]; cp = dx0 * dy1 - dx1 * dy0; circle_sign_cur = cp < 0; cur_zero = cp == 0; cur_difside = circle_sign_cur != circle_sign; // cout << "i = " << i << " cur pos " << cur_idx << " " << points[cur_idx][0] << " " << points[cur_idx][1] << endl; if (cur_zero) cur_difside = 0; if ((prev_difside || prev_zero) && (cur_difside || (cur_zero && !prev_zero))) { cout << "pop " << cur_idx + 1 << " : " << points[cur_idx][0] << " " << points[cur_idx][1] << endl; } if (cur_difside) { if (prev_zero) nxt_idx[prev_idx] = i; else nxt_idx[cur_idx] = i; } else if (prev_difside) { if (cur_zero) nxt_idx[i] = next_idx; else nxt_idx[i] = cur_idx; } // cout << "cur_idx " << cur_idx << " next_idx " << next_idx << endl; if (cur_idx == start_idx) break; } if (nxt_idx[i] == -1) cout << "pop " << i + 1 << " : " << points[i][0] << " " << points[i][1] << endl; else start_idx = i; } cout << "convex : " << start_idx + 1 << " "; for (int nxp = nxt_idx[start_idx]; nxp != start_idx; nxp = nxt_idx[nxp]) { cout << nxp + 1 << " "; } cout << endl; } int main() { int x, y, cnt = 0; string type; vector<array<int, 2>> points, rmlist; while (cin >> type) { cin >> x >> y; if (type[0] == 'A') { cnt++; points.push_back({x, y}); } else if (type[0] == 'R') { rmlist.push_back({x, y}); } } run(points); return 0; } ``` 邏輯上是針對每次新增的點對現有凸包繞一圈檢查是否在凸包內側,可以用外積向量的方向(正負值)判斷,另外要注意的是外積為0時的特殊判斷,上面的cur_idx是當前邊的出發點,next_idx是當前邊的結束點,要外積的另一個向量的出發點也是cur_idx,結束點是要檢查的新增點。凸包的連接關係由nxt_idx維護,如果要拋棄點的話只要改變nxt_idx就能將該點斷開。 從上面的code可以觀察有很多計算步驟非常相似,可以寫成共用的模組,像是外積向量的準備與外積本身,剩下要做的就是更新nxt_idx和state machine狀態轉移。細節處理完就大功告成了,裡面能優化delay的部分就是外積的乘法操作,想要做的更好可能要在這邊下功夫。之後有時間再試試看能不能pipeline繞一整圈的過程,減少需要的時間。 太久沒寫RTL以及為了HDL仔細優化c++ code,這題寫起來還蠻卡的,現在越來越覺得寫verilog前一定要先將對應的software model寫出來。雖然這樣寫的RTL很像是翻譯,但是優化電路和設計state machine時會比較有感覺,寫RTL和之後驗證才有個依據, 之後再補充RTL。 ### To be continued.