<style> .markdown-body table{ display: unset; } </style> # APCS實作題2016年3月第3題:線段覆蓋長度 > 第1版:2023年2月8日 > 第2版:2023年10月19日 > 第3版:2024年12月4日,讀了吳邦一教授的 AP325 講義之後重寫程式碼 > 作者:王一哲 > 題目來源:[105年3月5日實作題第3題](https://apcs.csie.ntnu.edu.tw/wp-content/uploads/2020/10/APCS-%E5%AF%A6%E4%BD%9C%E9%A1%8C-2016.03.05.pdf) > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=b966) <br /> ## 題目 ### 問題描述 給定一維座標上一些線段,求這些線段所覆蓋的長度,注意,重疊的部分只能算一 次。例如給定四個線段,(5, 6)、(1, 2)、(4, 8)、和(7, 9)。如下圖,線段覆蓋長度為 6。 <img height="80%" width="80%" src="https://i.imgur.com/ehcyKZl.png" style="display: block; margin-left: auto; margin-right: auto;"/> <br /> ### 輸入格式 第一列是一個正整數 N,表示此測試案例有 N 個線段。 接著的 N 列每一列是一個線段的開始端點座標和結束端點座標整數值,開始端點座標值小於等於結束端點座標值,兩者之間以一個空格區隔。 <br /> ### 輸出格式 輸出其總覆蓋的長度。 <br /> ### 範例一:輸入 <center> | 輸入 | 說明 | | --- | --- | | 5 | 此組測試案例有 5 個線段 | | 160 180 | 開始端點座標值與結束端點座標 | | 150 200 | 開始端點座標值與結束端點座標 | | 280 300 | 開始端點座標值與結束端點座標 | | 300 330 | 開始端點座標值與結束端點座標 | | 190 210 | 開始端點座標值與結束端點座標 | </center> ### 範例一:正確輸出 <center> | 輸入 | 說明 | | --- | --- | | 110 | 測試案例的結果 | </center> <br /> ### 範例二:輸入 <center> | 輸入 | 說明 | | --- | --- | | 1 | 此組測試案例有 1 個線段 | | 120 120 | 開始端點座標值與結束端點座標 | </center> ### 範例二:正確輸出 <center> | 輸入 | 說明 | | --- | --- | | 0 | 測試案例的結果 | </center> <br /> ### 評分說明 輸入包含若干筆測試資料,每一筆測試資料的執行時間限制(time limit)均為 2 秒,依正確通過測資筆數給分。每一個端點座標是一個介於 0~M 之間的整數,每組測試案例線段個數上限為 N。其中: - 第一子題組 30 分,M<1000,N<100,線段沒有重疊。 - 第二子題組 40 分,M<1000,N<100,線段可能重疊。 - 第三子題組 30 分,M<10000000,N<10000,線段可能重疊。 <br /> ## Python 程式碼 ### 寫法1,依照左端點由小到大排序 花費時間最久為 78 ms,使用記憶體最多為 4.8 MB,通過測試。 ```python= N = int(input()) # 線段數量 segs = sorted([tuple(map(int, input().split())) for _ in range(N)]) # 讀取線段端點,依照左端點由小到大排序 now = [-1, -1] # 目前所在線段左、右端點,預設為 -1,因為題目的端點最小值為 0 ans = 0 # 答案,線段覆蓋長度 for le, ri in segs: # 依序讀取線段左、右端點 if le > now[1]: # le 在目前線段右端點右側,新的線段 ans += now[1] - now[0] # 將目前線段長度加到 ans now = [le, ri] # 更新目前線段端點 else: # le 在目前線段左、右端點之間,線段合併 now[1] = max(now[1], ri) # 更新右端點,選較大的值 # end of for loop ans += now[1] - now[0] # 要加上最後一段的長度 print(ans) ``` <br /><br /> ### 寫法2,計算重疊線段厚度 花費時間最久為 85 ms,使用記憶體最多為 5.5 MB,通過測試。 ```python= N = int(input()) # 線段數量 segs = [] # 線段端點,左端點厚度加1,右端點厚度減1,依照端點由小到大排序 for _ in range(N): le, ri = map(int, input().split()) segs += [(le, 1), (ri, -1)] segs.sort() now = -1 # 目前的端點,預設為 -1,因為題目的端點最小值為 0 thick = 0 # 覆蓋厚度 ans = 0 # 答案,線段覆蓋長度 for p, t in segs: # 依序讀取端點及厚度變化 if thick == 0: now = p # 如果目前的厚度是零,p 為新的線段起點 thick += t # 更新厚度 if thick == 0: ans += p - now # 厚度歸零,沒有任何線段覆蓋此點,更新答案 # end of for loop print(ans) ``` <br /><br /> ## C++ 程式碼 ### 寫法1,依照左端點由小到大排序 花費時間最久為 7 ms,使用記憶體最多為 416 kB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <utility> #include <algorithm> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); int N; cin >> N; // 線段數量 vector<pair<int, int>> segs (N); // 線段端點,依照左端點由小到大排序 for(int i=0; i<N; i++) cin >> segs[i].first >> segs[i].second; sort(segs.begin(), segs.end()); pair<int, int> now = make_pair(-1, -1); // 目前的端點,預設為 -1,因為題目的端點最小值為 0 int ans = 0; // 答案 for(auto seg : segs) { // 依序讀取線段左、右端點 if (seg.first > now.second) { // seg.first 在目前線段右端點右側,新的線段 ans += now.second - now.first; // 將目前線段長度加到 ans now = seg; // 更新目前線段端點 } else { // seg.first 在目前線段左、右端點之間,線段合併 now.second = max(now.second, seg.second); // 更新右端點,選較大的值 } } ans += now.second - now.first; // 要加上最後一段的長度 cout << ans << "\n"; return 0; } ``` <br /><br /> ### 寫法2,計算重疊線段厚度 花費時間最久為 7 ms,使用記憶體最多為 752 kB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <utility> #include <algorithm> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); int N; cin >> N; // 線段數量 vector<pair<int, int>> segs; // 線段端點,左端點厚度加1,右端點厚度減1,依照端點由小到大排序 for(int i=0, le, ri; i<N; i++) { cin >> le >> ri; segs.push_back(make_pair(le, 1)); segs.push_back(make_pair(ri, -1)); } sort(segs.begin(), segs.end()); int now = -1, thick = 0, ans = 0; // 目前的端點,預設為 -1,因為題目的端點最小值為 0;覆蓋厚度;答案 for(auto seg : segs) { // 依序讀取端點及厚度變化 if (thick == 0) now = seg.first; // 如果目前的厚度是零,seg.first 為新的線段起點 thick += seg.second; // 更新厚度 if (thick == 0) ans += seg.first - now; // 厚度歸零,沒有任何線段覆蓋此點,更新答案 } cout << ans << "\n"; return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`