跳到主要內容

[Java] UVa 1730 Sum of MSLCM

題目連結

題意:
maximum sum LCM(MSLCM): 找到一個集合,最小公倍數(LCM)為N且該集合的和為最大

舉例:
MSLCM(2) = 3:
LCM(1, 2) = 2 且 1+2=3為 最大的和

題目給定 N (1<N<20000001) (2千萬)
要求MSLCM(2) ~ MSLCM(N)的和

如: N = 10
LCM(1, 2) = 2, 1+2=3
LCM(1, 3) = 3, 1+3=4
LCM(1, 2, 4) = 4, 1+2+4=7
LCM(1, 5) = 5, 1+5=6
LCM(1, 2, 3, 6) = 6, 1+2+3+6=12
LCM(1, 7) = 7, 1+7=8
LCM(1, 2, 4, 8) = 8, 1+2+4+8=15
LCM(1, 3, 9) = 9, 1+3+9=13
LCM(1, 2, 5, 10) = 10, 1+2+5+10=18

Total = 3+4+7+6+12+8+15+13+18 = 86

解法:
觀察上述例子,會發現
集合的總和為最大,恰為 N 之所有因數形成的集合
但不太可能從 2 ~ N 一一找因數還加總

我們發現,也把MSLCM(1)算進去的話
從1~N(這裡N=10)的所有因數出現次數統計如下:
1:10次  2:5次  3:3次  4~5:2次  6~10:1次
次數恰為 (N/因數) 的值(小數無條件捨去,即程式語言的整數相除)

所以原本的題目可以這樣解:
從2~N依序代入上述關係求得並加總

本來以為這樣就節省很多時間了
但N可以到20000000,所以O(n)還是TLE了

我們進一步發現
這些因數從小到大,出現次數會遞減
而且連續因數的次數可能都一樣(像上述的4, 5都是2次,6~10都是1次)
所以我們希望,次數相同的因數一起算

此時用到類似binary search的做法
得到每個次數的起始與結束區間(左右區間)
把區間內的因數加總後,再乘上次數

以N = 10為例
每次先用前一次結果得到左區間,初始為0,先得到第2列的左=1
剛剛知道因數對應次數可以 (N/因數)得到,所以第2列的次數 t = 10/1
再來是右區間, N/t 即可

這要從因數6~10觀察
10/6~10/10都會得到次數1,發現除以次數: 10/1得到的10會在最右邊

左區間與右區間之間的因數要全部加起來,像6~10,就是6+7+...+10
可以用梯形公式 (6+10)*5/2 = 40 ,再 * 1 (次數) = 40

每個次數算完加總後,最後要扣掉MSLCM(1)的1,所以 87-1 = 86


很多題目可以線性時間做完
但若測資範圍極大,一定TLE,就要思考優化的解法了QQ

程式(Java):

留言

  1. 梯形公式 (6+10)*10/2 = 40 這裡有誤(??
    應該是(6+10)*5/2 = 40

    回覆刪除
  2. 感謝回覆,6~10的高是5沒錯

    回覆刪除

張貼留言

這個網誌中的熱門文章

【Ubuntu】Terminal 複製貼上的方法

Ctrl C + Ctrl V 能使用嗎? 有常常透過Ubuntu在指令海裡摸索的一定不陌生 在Terminal裡我們不能用Ctrl C + Ctrl V來進行複製貼上的作業 每次都不小心Ctrl C下去,就要抱頭痛哭QQ (在Terminal裡面是呼叫一個外部中斷,強制中止當下系統的動作) 其實在Terminal當中也有複製貼上的功能 Ctrl + Shift + C 複製 Ctrl + Shift + V 貼上 功能相當於一般的複製貼上,也只是多按一個Shift,相信不是太難適應 如果是透過VirtualBox建立的Ubuntu 想要跟主機互通的話,可以參考這篇: [VirtualBox] 共用剪貼簿與檔案 另外,在Terminal裡也有所謂的暫存剪貼簿,是針對指令設計的 Ctrl + U 剪下游標前的字元到剪貼簿 Ctrl + K 剪下游標後的字元到剪貼簿 Ctrl + Y 貼上 所謂的游標就是打指令的那行閃爍的位置 所以其他畫面上系統輸出的文字不會受影響,才說是針對指令設計 這個暫存的剪貼簿也跟一般的是分開的,或許會有派上用場的一天吧!

【Ubuntu】關閉預設畫面自動鎖定

近期一直碰到一個小麻煩 在Ubuntu VM做研究時 因為平常會在本機Search,才去VM操作 卻發現每次畫面跳去Ubuntu那邊 都還要重新打密碼登入,很惱人 怒查之下,原來這個動作叫  畫面鎖定 反正VM就是個測試環境,根本不需要這些安全措施 透過下面步驟就可以取消了 甚至連開機登入時都可以不用打密碼了呵呵

[Java] HashMap資料結構簡介與用法

Java HashMap 官方文件: https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html import java.util.HashMap; 簡介: 以每個獨立的 key 對應一個 value 特性: 資料(K, V)原則上是一對一 key不能重複,若重複宣告,後者會取代前者 資料不會排序,預設是先宣告的在前面(呼叫整個 HashMap 的話) 用途: 建立對應表(轉換表),一 key 對一 value 像是學生成績表,以名字做為 key,查詢 key 可以得對應的成績 value 資料格式: HashMap<K,V> K: key V: value 用法: 1. 宣告 : 型別是 reference,不能存基本型別,像整數要寫 Integer 而非 int HashMap<String, String> hashmap = new HashMap<String, String>(); HashMap<String, String> hashmap = new HashMap<>(); HashMap<String, Integer> hashmap = new HashMap<>(); 2.新增資料 : 同時給Key-Value參數,新增這筆資料到該hashMap hashmap.put("Item 1", "Value 1"); hashmap.put("Item 1", 25); 3.刪除資料 : 根據參數Key刪掉對應的Key-Value資料 hashmap.remove("Item 1"); 4.取特定key對應的值 hashmap.get("Item 1"); 5.顯示整個HashMap內容 System.out.println("內容:" + hashmap); for(String Key: map.keySet()){ System.out.println(Key + " &