Leetcode Weekly Contest 95解題報告
Contest 95
本文是同步自我的 github 專案leetforfun,歡迎關注和討論
連結串列的中間節點
題目大意
給定一個連結串列,返回連結串列的中間節點。如果節點數是偶數,返回靠後的那一個節點 比如:
- 3->5->7->6->9 返回 7
- 3->4->5->6 返回 5
題解
難度:1 顆星
這道題最容易想到的就是:
- 遍歷一次連結串列,得到其長度 K
- 得到長度 K 後可以算出哪個是中點,再遍歷一次到那個節點即可
那麼,有沒有看起來
更好一點的辦法呢?
可以假設有兩個小朋友 A 和 B,都站在連結串列的頭部,他們要一起遍歷這個連結串列。假設 A 一次前進兩個節點,B 一次只能前進一個節點。那當 A 走到連結串列盡頭時,B 剛好就走到連結串列中間。AC 程式碼就是採用這種方法。 但是這種方法是不是就快呢?第一種方法是 O(n)+O(n/2),第二種方法雖然看起來是 O(n),但實際上迴圈體內部進行了兩次遍歷,看起來遍歷的量還是一樣的啊?恩,這是一個好問題(有空再想想)。參見 CSAPP 上的迴圈展開,然而,是不是真的會快還是得實際測試,並且需要到彙編層面去看這種展開能否利用上 CPU 的流水線。如果能用上,那就牛逼了。
石子游戲
題目大意
有一個陣列 a[i],A 和 B 每次能從陣列的頭或者尾取一個數,最後誰的總數大誰贏。如果 A 先取,問 A 有沒有必勝的策略。(保證陣列有偶數個元素,總和是奇數)
題解
動態規劃
先計算出 a[i] 的前項和 sum[i],即 sum[i] = a[0]+a[1]+...a[i-1] 設 dp(i,j) 表示:在面對陣列 a[i..j] 時,如果某個人先取,最大能取得的總和 如果 A 有必勝的策略,那麼 dp(1,n) > sum[n]/2。
那怎麼來求 dp(i,j) 呢?對於 a[i..j],假設 A 先取,他有兩種選擇:
- 如果 A 取 a[i],那麼剩下的陣列就變成了 a[i+1..j],此時該 B 取了。B 也不笨吶,B 也會用最佳的策略來取,所以 B 在 a[i+1..j] 中能取到的最大值是 dp(i+1,j)。設 a[i+1..j] 的總和是 t,想一想
t-dp(i+1,j)
代表什麼。t 是 a[i+1..j] 的總和,dp(i+1,j) 是先取數的那個選手所能取到的總和,顯然t-dp(i+1,j)
就是另一個人能取到的總和了。回過頭來看,對於 a[i..j],Aq 取 a[i],B 在剩下 a[i+1..j] 中取的最大總和是 dp(i+1,j),因此 A 實際能取到的值是a[i]+t-dp(i+1,j)
- 如果 A 去 a[j],那麼剩下的陣列就成了 a[i..j-1],同樣的,設 a[i..j-1] 的和是 t2,這時 A 能取到的最大值就是
a[j]+t2-dp(i,j-1)
綜上, dp(i,j) = Max{a[i]+t-dp(i+1,j), a[j]+t2-dp(i,j-1)}
因此很明顯就是一個記憶化搜尋,AC 程式碼
數學歸納法
在完成動態規劃的程式碼之後,我嘗試寫一些測試用例。但是寫著寫著我發現,我竟然構造不出 A 先取但結果是 false 的 case,這讓我產生了一些想法。於是試著做一下數學歸納:
- 假設只有兩個數 a,b,A 先取,顯然他必勝,哪個大取哪個嘛
- 如果有 4 個數 a,b,c,d:
- 假設 A 先取 a, 不論 B 怎麼取,A 一定能取到 c
- 假設 A 先取 d, 不論 B 怎麼取,A 一定能取到 b
也就是說如果 A 先取,他一定可以要麼取 a,c 要麼取 b,d。如果 a+c>b+d,A 就取 a,c,否則他就取 b,d。所以對於 N 等於 4 的 case,先手一樣是必勝
- 當有 6 個數 a,b,c,d,e,f,A 一定能取到 a,c,e 或者 b,d,f,如果 a+c+e>b+d+f,那麼就取 a,c,e 否則取 b,d,f
- 當有 N=2*n 時,a[1...2n]
- 如果 A 先取 a[1],他一定能取到 a[1],a[3],a[5],... 即所有的奇數項
- 如果 A 先取 a[2n],他一定能取到所有偶數項: a[2],a[4],...a[2n]
所以如果奇數項和大於偶數項和,那麼 A 就取奇數項,否則取偶數項,因此 A 也是必勝的。
這裡你可能有個疑惑點,憑什麼說 A 先取 a[1],就一定能取到所有奇數項? 舉個例子,假設 A 取 a[1],如果 B 取 a[2],那麼 A 就可以取到 a[3]。如果 B 取 a[2n],那麼 A 可以取 a[2n-1]。A 始終可以取到奇數項。 同理,A 先取 a[2n],如果 B 取 a[1],A 就可以取 a[2],如果 B 取 a[2n-1],A 就可以取 a[2n-2],A 始終能取到偶數項。
因此,先手一定有必勝的策略。
所以最簡單的,這道題可以直接:
return true
第 N 個神奇數字
題目大意
如果一個數能被 A或者
被 B 整除,這個數就是神奇數。給定 A B,求第 i 小的神奇數
計算 + 模擬
首先求出 A 和 B 的最小公倍數 lcm(least common multiple)。lcm(A,B) = A*B/gcd(A,B)
。 gcd(A,B) 是 A B 的最大公約數。 我們用 p 表示 lcm(a,b),可以很容易發現,a,2a,3a,4a ... (p/a)*a
都是神奇數;同樣的b,2b,...(p/b)*b
也是神奇數。所以從 0 到 p,一共有t=p/a + p/b - 1
個神奇數。在 0 到 p 中,除了 p 以外,x*a
和y*b
可能相等嗎?——不可能,如果x*a == y*b
,那 p 就不是 a,b 的最小公倍數了。p 2p 3p ... tp,每增加 p,就增加了 t 個神奇數。由於求第 N 小的,所以q=N/t*t*p
就是離第 N 小的神奇數最近的那個 AB 的公倍數,q 是第N/t*t
個神奇數。剩下的就列舉一下即可找到。 通過計算最大公約數,最大限度的減少列舉量。不知道還有沒有更好的解法,沒關注了…
AC 程式碼
盈利計劃
題目大意
有 G 個人,有 m 個活動,每個活動需要 group[i] 個人參與,並且獲得 profit[i] 的利潤。問一個有多少種方法,使得總利潤不少於 P。
DP
仔細一看,這道題其實就是最基礎的01 揹包問題。G 其實就是揹包的容量,每個活動 group[i] 就是物品 i 的體積,profit[i] 就是把物品 i 裝入揹包得到的價值。最後求解的就是總價值不少於 P,一共有多少種裝法。
設 f(i,j,k) 表示前 i 個物品,容量 j,總價值 k,一共有多少種裝法。
- 如果第 i 個物品不裝,f(i,j,k) = f(i-1,j,k)
- 如果第 i 個物品裝,f(i,j,k) = f(i-1,j-group[i], k-profit[i])
由於我們求大於等於 P 一共有多少種裝法,因此我們要列舉所有f[n][j][k] (j∈[1,G], k>=P)
,把這些值累加起來。
對於 01 揹包,有個非常常用且簡單的優化空間的方法,就是倒序列舉,能節約 1 維空間,把f[i][j][j]
變成f[j][k]
,感興趣自己可以在網上搜一下,詳見AC 程式碼
總結
這套題還是沒有涉及太多的演算法,主要都是靠動腦筋。石子游戲比較好,挺考驗思維的。最後一題需要對 DP 模型比較敏感,能舉一反三。這倒不是說去背題,而是說一定要把某一種型別的題吃透,之後的題會無限的多,你不可能每道題都做過,它們必然派生自某類題目。如果你吃透了那類題目的解法,那你就能很快想到解題策略。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- LeetCode Weekly Contest 96 解題報告LeetCode
- Leetcode Weekly Contest94 解題報告LeetCode
- Weekly Contest 387
- Leetcode 第136場周賽解題報告LeetCode
- 【LeetCode】253. Meeting Rooms II 解題報告(C++)LeetCodeOOMC++
- KEYENCE Programming Contest 2024(AtCoder Beginner Contest 374)題解
- 【LeetCode】416. Partition Equal Subset Sum 解題報告(Python & C++)LeetCodePythonC++
- AtCoder Beginner Contest 352題解
- AtCoder Beginner Contest 378 題解
- 題解:AtCoder Beginner Contest 367
- AtCoder Beginner Contest 381 題解
- 題解:AtCoder Beginner Contest 371
- AtCoder Beginner Contest 376 題解
- Hitachi Vantara Programming Contest 2024(AtCoder Beginner Contest 368)題解A~D
- Toyota Programming Contest 2024#11(AtCoder Beginner Contest 379)題解
- AtCoder Beginner Contest 350 A - G 題解
- AtCoder Beginner Contest 380 (A~E)題解
- AtCoder Beginner Contest 238 A - F 題解
- [熵值] 解題報告熵
- AtCoder Beginner Contest 345 A-F 題解
- Atcoder Beginner Contest 380 題解 (A-G)
- Atcoder Beginner Contest 356題解(D、E)
- Toyota Programming Contest 2024#11(AtCoder Beginner Contest 379)題解總結
- Kotlin Weekly 中文週報 —— 17Kotlin
- Kotlin Weekly 中文週報 —— 18Kotlin
- ARC173 解題報告
- P11188 解題報告
- SP30906 解題報告
- P10499 解題報告
- Daiwa Securities Co. Ltd. Programming Contest 2024(AtCoder Beginner Contest 383)題解AI
- [leetcode 題解] 849LeetCode
- Leetcode 全套題解LeetCode
- [ABC349] AtCoder Beginner Contest 349 題解
- AtCoder題解 —— AtCoder Beginner Contest 183 —— C - Travel
- LeetCode題解第122題LeetCode
- CF720B 解題報告
- CNTALENT:2021年90、95後職場調研報告
- [題解](A-G)Atcoder Educational DP Contest(更新中)