https://open.kattis.com/problems/sortofsorting
今天有很多字串要做排序,排序的方式是這樣的
要比較兩個字串的大小,我們只需要看這兩個字串的前兩個字元即可
而這兩組的兩個字元所組成的兩組字串比較方式是字典序
如果說這兩組字串的字典序是相同的,那麼就依照原本的順序排即可
補充: 字典序
字典序指的是依照 A-Z
以及 a-z
的方式排列,更廣義的來說,你可以參考 ascii table 每個字所對應的數值,我們就是依照這個大小來比較的
在 C++ 當中,我們可以用 int(c)
來找到字元 c
所對應到的數值
舉例來說,要排序兩個字串 Poincare
以及 Pochhammmer
因為前兩個字的字典序相同,所以依照原本的順序即可 Poincare Pochhammmer
再舉一個例子,要排序兩個字串 Beethoven
以及 Bach
的一個字元的字典序相同,而第二個字的字典序 a
比 e
小,所以放在前面 Bach Beethoven
其實這個排序方式就是所謂的 stable sort, 在 C++ 當中已經有這個函數可以使用
不同的是排序的方式,所以我們要另外寫 compare 函數
比較的方式在前面也有提到就不贅述,詳細可以直接看 code 當中的 cmp()
每筆測資輸入時間複雜度
每筆測資排序時間複雜度
每筆測資輸出時間複雜度
每筆測茲的總時間複雜度約為
https://open.kattis.com/problems/sidewayssorting
一般來說我們排序字串都是由左到右,但是本題要我們由上到下來看
輸入第一行包含兩個正整數 ,表示 row 與 column
接下來有 行,每行 個字元
最後輸出垂直方向由上到下的穩定排序
並且排序過程當中忽略大小寫
舉例來說
從垂直的方向來看,我們會得到三個字串
接下來排序這幾個字串
最後排回原本的橫向
因為排序的方向是垂直的,我們可以直接以垂直的方式儲存這些字串即可
接下來穩定排序過後在改變輸出的方向即可
至於排序的部分,我們可以先把所有要比較的字串先都轉換成小寫之後再來比較大小
每筆測資輸入時間複雜度為
每筆測資初始化時間複雜度為
每筆測資排序時間複雜度為
每筆測資輸出時間複雜度為
每筆測資總時間複雜度約為 ( 很小,可以忽視)
https://toj.tfcis.org/oj/pro/47/
給一串長度為 的序列,有 筆詢問
對於每一筆詢問
no
以及第零個元素no
首先因為我們的答案可能藉在某兩個連續的元素之間,所以我們應該要嘗試讓序列是有嚴格遞增或是嚴格遞減的性質
所以我們第一步是要先將序列排序
排序過後,我們可以先處理兩個包含 no
的特例
直接判斷 和 arr[0]
以及 arr[n-1]
的關係即可
如果 ,就輸出 no arr[0]
如果 ,就輸出 arr[n-1] no
接下來就是要來找我們的詢問 是在序列中的哪個位置
我們可以用二分搜來序列當中第一個 的元素位置 (也就是 lower_bound
)
接下來,如果找到的 在 上就是 那就直接輸出
否則直接輸出 arr[res-1] arr[res]
實際上我們可以不用自行實作 lower_bound
在 C++ 可以直接 #include<algorithm>
,裡面就有 lower_bound
以及 upper_bound
可以直接使用,使用方式如下
lower_bound
以及 upper_bound
回傳的都是指標,所以要用 *
取出值
如果想要得到元素是位在 中的哪個位置,可以用 lower_bound(arr, arr+10, 5)-arr
取得
輸入時間複雜度為
排序時間複雜度為
每筆詢問進行二分搜的時間複雜度為
總時間複雜度為
https://toj.tfcis.org/oj/pro/468/
輸入包含 個元素的序列,接下來 筆詢問
每筆詢問包含兩個正整數 ,求在序列當中有多少百分比的元素介於 至
小數精確至小數點後第 位
先對序列做排序
接下來題目要問的是有多少元素介於 之間
如果我們能找到第一個 的元素位置以及第一個 的元素位置,那麼這兩個元素之間的個數就是我們的答案
要找第一個 的元素位置就等同於找 lower_bound
要找第一個 的元素位置就等同於找 upper_bound
跟上一題一樣的,實際上沒有必要自己實作 lower_bound
以及 upper_bound
可以直接引用 C++ algorithm
裡的函數即可
輸入時間複雜度為
排序時間複雜度為
找 lower_bound 以及 upper_bound 總時間複雜度為
總時間複雜度為
http://domen111.github.io/UVa-Easy-Viewer/?907
有一群人要去旅行,旅行的路線必須要經過 個暫停點休息
已經知道包含起點以及終點這 條邊之間的距離是多少
請問這一趟旅程當中一天最多走多長的距離,才可以在 個晚上 (也就是 天) 內到從起點達終點?
如果說一天走 個距離可以在 天內到達,那麼 也一定可以
所以我們發現到了行走距離是具有單調性的
有了單調性,要我們搜尋最佳答案就可以使用二分搜
我們可以二分搜一天最少所需的行走距離,如果說距離 不能在 天內走到,那就更新左界至 ,否則更新右界至
輸入時間複雜度為
二分搜的範圍最差會是總距離,也就是
搜尋時間複雜度為
總時間複雜度約為
https://zerojudge.tw/ShowProblem?problemid=c575
有一條路上有 個電信的服務點 ,現在我們要裝設最多 個基地台,讓這 個電信服務點都能接收到訊號
基地台可以放在座標點上的任意位置,並且有一個直徑為 的訊號涵蓋範圍
給定這 個點的座標,是否能找到一個最小 ,使得所有服務點都能接收到訊號
如果說直徑 可以在 個基地台內涵蓋整個區域,那麼 也一定可以
所以我們觀察到 具有單調性,並且基地台是可以任意擺放的,哪麼要找 的最小值就可以用二分搜來求解
我們可以二分搜 的大小
從 開始每次增加 ,把涵蓋在 ~ 的服務點移除
接著從沒被移除掉的點中找到最左端的服務點繼續檢查
最後如果需要用的基地台數量大於 就更新左界成 ,否則更新右界成
輸入時間複雜度為
排序時間複雜度為
二分搜的值為
搜尋時間複雜度為
本題的 不超過
總時間複雜度約為
https://toj.tfcis.org/oj/pro/556/
有 項工作,為了好好管理進度,我們把每一份工作都分成了 份
對於第 項工作每天可以完成 份,並且每天都會做每一項工作
因為怕工作會做不完,我們有 筆經費可以將工作外包
每一經費對於第 項工作可以將其中的 份工作外包,並且不需考慮外包的作業時間
請問在最佳分配經費的情況下,最少可以在幾天之內完成 份工作
如果說 天可以把工作處理完成,那麼 天也一定可以
發現到天數是具有單調性的,那麼要求最小的天數就可以用二分搜來完成
二分搜最小所需要的天數 ,確認 是否可行來調整左界與右界
那麼要怎麼確認 天能不能完成呢?
我們可以把每一項工作都先減去 天自己做可以完成的工作量
那麼剩下的所有工作就必須要外包才能完成了
最後計算需要外包的數量,就可以判斷是否可以在 天完成了!
輸入時間複雜度為
二分搜的右界為
搜尋時間複雜度為
總時間複雜度為
http://domen111.github.io/UVa-Easy-Viewer/?10282
題目會先給你每個英文字對應到的外國文字,這群對應的字保證是一對一,形成一個字典
接下來會有多筆詢問,輸入一個外國文字,輸出相對應的英文字,如果不存在則輸出 eh
這題可以直接用 map 對應,但是這裡我們用二分搜來實作看看
首先先把字典建立好,可以用一個 struct 來建立每個英文字對應到的外國字,接下來排序,方便我們查詢
最後用二分搜就可以了
假設有 筆輸入以及 筆詢問
輸入時間複雜度為
排序時間複雜度為
搜尋時間複雜度為
總時間複雜度為
http://domen111.github.io/UVa-Easy-Viewer/?10341
今天有一個算式
已知 ,給定 ,求
首先先來觀察一下算式,將算式中的每個部份拆分出來
我們只討論 所在的的範圍
接下來把係數也考慮進去
由此可知, 是遞減函數
令這個函數為
知道這個函數是呈現遞減的狀態,我們要求其解就想到利用二分搜
透過二分搜,我們可以枚舉 ,並且有三種情況
但是程式會有浮點數的誤差存在,所以很難判斷到底應不應該繼續往下尋找答案
但是因為 是連續函數,我們可以用勘根定理找到是否存在解,只要除去不存在解的情況,我們就可以放心地往下繼續找答案
除去無解的情況,接下來找答案就很快了
但是浮點數誤差一定要小心處理
當中有很多數學函數,在 C++ 當中可以直接 #include<math.h>
其中 都可以直接使用
則可以用 exp(x)
表示
每筆測資輸入時間複雜度為
每筆測資找答案的時間複雜度為
每筆測資總時間複雜度約為
https://toj.tfcis.org/oj/pro/55/
輸入 個數字,接下來有 筆詢問,想知道詢問的數字 在序列當中出現的次數
直接計算 upper_bound ( 的第一個位置) - lower_bound ( 的第一個位置)
因為這一題的輸入數量有點大,需要加上輸入優化
加上了輸入優化後,就不可以將 c 和 c++ 的 IO 語法混用,要注意這一點
可以參考 https://hackmd.io/@wiwiho/CPN-io-optimization
輸入時間複雜度為
排序時間複雜度為
二分搜時間複雜度為
總時間複雜度約為
http://domen111.github.io/UVa-Easy-Viewer/?10110
在一條走廊上有編號 ~ 的電燈泡
有個人在這條走廊上來回走 趟,第 次出發會將編號為 的倍數的電燈泡開關轉換一次(也就是 開 -> 關
關 -> 開
),而在走回來不會做任何事
請問來回走完 趟之後,第 顆電燈泡是亮的還是暗的? (一開始所有電燈泡都是關的)
如果第 個電燈泡是關的,那麼他一定被操作了 次 ()
反過來說,如果第 個電燈泡是開的,那麼他一定被操作了 次 ()
那何時電燈泡會被操作呢?
只有在他的因數出現的時候會被操作到
那麼如果某數字有奇數個因數,則最後會是開的,反之是關的
任何數字 的因數 都會有相對應的數字 使得 ,並且 也是 的因數
如果說因數是奇數,那就表示最中間的因數 找到相對應的數字也是 ,使得
發現到,當因數個數為奇數,也就是 是完全平方數的時候第 個燈泡會是亮的,反之是暗的
那麼,我們只需要做開根號的動作,去看看開根號後的值平方以及原本的數字是否相同即可
不過這裡要練習二分搜,所以就二分搜開根號吧
每筆測資輸入時間複雜度為
每筆測資找到答案的時間複雜度為
每筆測資總時間複雜度為
http://domen111.github.io/UVa-Easy-Viewer/?10474
每一筆測資有 筆資料以及 筆詢問
要求資料排序過後,每筆詢問的數字在資料當中第一次出現的位置,從一開始數
要找排序後的位置,當然先排序好
在排序完成後,要找第一次出現的位置,那就直接帶入 lower_bound 的概念即可
每筆測資輸入時間複雜度為
每筆測資找搜尋時間複雜度為
每筆測資總時間複雜度為
http://domen111.github.io/UVa-Easy-Viewer/?10530
現在正在玩猜數字的遊戲,對方會告訴你你猜的數字太高或是太低,但是我們懷疑對方會說謊
假設當我們猜到正確數字時對方並不會說謊,給定每回合猜數字的結果,請幫忙判斷對方是否有說謊
猜數字的範圍在 ~ 之間
給定一個數字,對方回覆太大(too high
),那麼如果沒說謊,我們的數字應該比他還小,那麼我們只需要紀錄 too high
當中最小的數字即可
反過來說,對方回覆太大(too low
),那麼如果沒說謊,我們的數字應該比他還大,那麼我們只需要紀錄 too low
當中最大的數字即可
如果我們猜到的數字介在 too high
以及 too low
之間,那就表示對方沒有說謊
SCIST 演算法 題解