python-APCS-2023/1/8實作第四題:機器出租 === ### ZeroJudge:j608機器出租 #### 沒有圖解,請耐心閱讀,感恩 --- \ \ \ \ \ <font color="#FFEE99">**1超時問題與40分思考想法**</font> --- :::info **超時問題** 在20分測資中基本上是沒有超時問題 40分測資開始,只能用1次且單層迴圈搜尋整個活動的資料,否則會超時 \ **40分思考想法** 在活動時間中,要不重複拿取時間段,且要拿到最佳的方法 既然要一個個拿,那就必須要排序,而問題就在如何排序 \ 從結論來看-->結論是必須依照每個活動的結束時間排序 原因: 整個活動時間內有一堆互相重疊的線段[頭, 尾] 把所有互相重疊的線段拿來看-->選了其中一個,剩下的都不能選了 \ 假設有以下線段:[[1, 3], [2, 4], [3, 4], [3, 5], [5, 6], [2, 7], [4, 7], [7, 8]]:已排序 從第一個拿[1, 3], 其他的線段的尾一定>=3 跟[1, 3]重複的線段: 尾=3那一定重疊 尾>3如果重疊,那一定是頭<=3才會碰到[1, 3]線段 因此跟[1, 3]重疊的線段都有一個共通點:線段都碰到3 所以他們是互相重疊的,而到底該選哪個? 以下是重疊的線段 [1, 3]影響有碰到3以下的數字 [2, 4]影響有碰到4以下的數字 [3, 4]影響有碰到4以下的數字 [3, 5]影響有碰到5以下的數字 [2, 7]影響有碰到7以下的數字 如果你拿了[2, 4]那麼有可能就會影響到後面本來可以拿的[4, 7] 尾越小的線段影響得越少-->按照尾來排的話,第一個必拿 從這裡可以得到一個邏輯:我從第一個開始拿[1, 3],並且後面開始,線段只要頭碰到3以下,我都不能拿,直到再找到可以拿的線段[5, 6],從這裡開始,線段頭碰到6以下,我都不能拿,以此類推 ::: :::success **二維list的排序** 參考來源:https://docs.python.org/3/howto/sorting.html 或者搜尋python org,在裡面找到sort的章節 \ 總之就是使用operator模組中的itemgetter list.sort()中有key和reverse,你就把key想成排列的依據,key=itemgetter(num1, num2)的意思是:按照list中索引num1小到大排列,如果相等,再用num2排列... \ **zip()用法** zip(list1, list2) 會回傳 [(list1[0], list2[0]), (list1[1], list2[1])... ...] 其中()是turtle型別 ::: 程式碼line8: activities存放所有時間資料 choose記錄拿的次數 limit記錄每次拿完後的尾 ```python= from operator import itemgetter n, k = map(int, input().split()) s = [int(i) for i in input().split()] f = [int(i) for i in input().split()] activities = [list(i) for i in (zip(s, f))] s = f = None activities.sort(key=itemgetter(1)) choose = 0 limit = -1 for i, j in activities: if i > limit: choose += 1 limit = j print(choose) ``` ![](https://i.imgur.com/EqX4czp.png) \ \ \ <font color="#FFEE99">**2滿分解法**</font> --- :::info **滿分思考** 從40分解法我們知道一個概念:拿完線段後,limit存放下一個可拿線段的限制 而當有多個機器的話,當然就是存放每一個機器的limit \ 假設兩臺機器-->開始搜尋,只要有機器可以拿搜尋到的線段,就拿 問題:如果兩個機器都可以拿這個線段怎麼辦? 假設目前limit分別是1和5並且有一個線段[6, 10]頭都比limit大 \ 如果這個時候配給限制是1的線段會發生什麼事? -->配置給limit=1的機器後limit變成10和5 -->配置給limit=5的機器後limit變成1和10 看得出來後者的limit會比較不嚴格 \ 結論:找出通過限制的線段後,把它配置目前給limit最大的機器 \ **找出頭應該給哪個機器** limit = [1, 9, 8, 3, 5, 6] 線段[6, 10] 首先:把6放入limit並且limit.sort(reverse=True) limit = [9, 8, 6, 6, 5, 3, 1] 找出6的索引-->index=2 **找出小於6的limit值-->index = 2+limit.count(6)-->5(找到了!!)** 取線段並且改變limit值 limit = [9, 8, 6, 6, 6, 3, 1] 將放入的頭拿出 limit.pop(limit.index(6)) limit = [9, 8, 6, 6, 3, 1] ::: 註:我的寫法是用很簡單的寫法但複雜邏輯操作,很吃理解 \ 程式碼: ```python= from operator import itemgetter n, k = map(int, input().split()) s = [int(i) for i in input().split()] f = [int(i) for i in input().split()] activities = [list(i) for i in (zip(s, f))] s = f = None activities.sort(key=itemgetter(1)) limit = [-1] * k # 存放所有機器的限制(哪一個機器的不重要) choose = 0 for i, j in activities: limit.append(i) # 暫時先把i放到機器中並用line13的sort搭配.index()找出相對位置 limit.sort(reverse=True) # 由大到小 if i != limit[-1]: # 如果i的位置在最後面,代表他沒有通過所有機器的限制 choose += 1 limit[limit.index(i)+1+limit.count(i)-1] = j # 在沒有重複下i的下一個就是通過的limit # 但如果有重複的話ex.[5, 5, 4, 3] i = 5 # 5的下一個還是5,所以line17才會再加上limit.count(i)-1 limit.pop(limit.index(i)) # 把暫時放入的i拿出去 print(choose) ``` ![](https://i.imgur.com/q2kMy2p.png) ###### tags: `題解` ###### tags: `APCS` ###### tags: `python`