OpenJudge 刷題思路彙總

模擬

  • lc1424. 對角線遍歷 II:如果按照這個順序模擬一遍,遇到是空的話就跳過,但這樣會超時。其實對角線上橫縱座標和是一個定值,利用這個性質,可以正常遍歷 nums,然後把每個元素加到一個二維數組中(第一維是 x + y),因爲加進去的時候是倒序的,所以最後再倒序遍歷一下即可

雙指針

  • lc 面試題57. 和爲s的連續正數序列雙指針 滑動窗口 i=1,j=1i = 1, j = 1, ii 先動,如果 sum>targetsum > target了,那當前的 j 與後面的 ii 之間的區間和也一定超過 sumsum,所以 jj 可以必須往後走了,直到 sum<=targetsum <= target 才停下來,接着是具體問題的邏輯,最後再動 ii。時間複雜度:O(n)O(n) 數學公式推導 也可以用類似 lc 829. 連續整數求和 的方法去求數學公式 時間複雜度:O(n)O(\sqrt n)
  • lc 3. 無重複字符的最長子串:跟“和爲s的連續正數序列”這題思路完全一樣,都是用兩個指針去維護一個滑動窗口

二分

  • lc374. 猜數字大小 + KickStart NumberGuessing + eoj3342. 經典的猜數字遊戲交互題 猜數字遊戲的本質就是個二分,題目會有一個數 pick,然後你在一個區間 [1,n][1, n] 中二分查找這個數。由於你實現不知道 pick 是多少,所以需要出題人告訴你這個數大了還是小了,還是猜中了(guess 函數的功能),這部分相當於二分中的 check 函數。注意:雖然範圍是 [1,n][1,n],但用這範圍如果出題人的數是最後一個,那最後一次會還沒有猜就退出循環,所以保險起見最好用 while(true)while(true)

  • lc LCP 12. 小張刷題計劃:最大值最小化,二分答案常用套路

動態規劃

  • lc 300.最長上升子序列動態規劃 O(n2)O(n^2)f[i]f[i] 表示以第 i 個數爲結尾的上升子序列的集合,屬性(最大值) 。貪心+二分 O(nlogn)O(nlogn) 對於一個上升子序列,其結尾元素越小,越有利於在後面接其他的元素,也就越可能變得更長。tail[i]tail[i] 表示長度爲 i 的所有最長上升子序列結尾的最小值。具體地,每次來一個新的數 num,利用二分找到最後一個小於 num 的位置 l,然後讓 tail[l+1]=numtail[ l + 1] = num,這樣新的數有更多可能性接在 tail[l+1]tail[l+1]的後面

  • ac1024. 裝箱問題01揹包問題 把物品的體積同時看做體積和價值,問題轉化爲在不超過揹包最大容量的情況下,最大價值是多少。由於 N=2e4N = 2e4,所以必須對代碼進行等價變形,降低空間複雜度

  • ac900. 整數劃分將 n 劃分爲若干個正整數之和的劃分方案數 把1,2,3, … n分別看做n個物體的體積,這n個物體均無使用次數限制,問恰好能裝滿總體積爲n的揹包的總方案數。由推導得f[i][j]=f[i1][j]+f[i][ji]f[i][j] = f[i - 1][j] + f[i][j - i],注意初值 f[i][0]=1f[i][0] = 1(全不選也是一種方案),再對這公式進行等價變形。拓展: 將 n 劃分爲最大數不超過 k 的劃分方案數(完全揹包返回前 k 種物品的方案數f[k][n]f[k][n])

  • 百鍊 4119:複雜的整數劃分問題

  1. N劃分成K個正整數之和的劃分數(狀態轉移難)
    狀態表示:f[i][j]f[i][j]表示用 i 個物品(可重複),且恰好拼成j的方案數
    狀態計算:此題狀態轉移比較難,f[i][ji]f[i][j - i]表示用i個物品(可重複)且每個物品都大於1(同時減1),f[i1][j1]f[i - 1][j - 1]表示用i個物品(可重複)且至少有一個物品是1(減去這個1)
    邊界設置:只要設置f[0][0]=1f[0][0] = 1,則所有狀態都能被算出來
  2. N劃分成不同正整數之和的劃分數(01揹包)
    狀態表示:f[i][j]f[i][j]表示只用前i種物品(最多一次),恰好拼成j的方案數
    狀態計算:f[i1][j]+f[i1][ji]f[i - 1][j] + f[i - 1][j - i](這兩個集合不一定存在)
    邊界設置:只要設置f[0][0]=1f[0][0] = 1,則所有狀態都能被算出來
  3. N劃分成若干奇正整數之和的劃分數 (完全揹包)
    狀態表示:f[i][j]f[i][j]表示只用前i種物品(只選奇數),恰好拼成j的方案數
    狀態計算:f[i2][j]+f[i][ji]f[i - 2][j] + f[i][j - i] (這兩個集合不一定存在)
    邊界設置:只要設置f[1][0]=1f[1][0] = 1,則所有狀態都能被算出來
  • ac 1307. 牡牛和牝牛:狀態表示:f[i]表示符合題目要求的 i 頭牛的排列方式,狀態計算:根據第 i 頭牛是"0"還是"1"去劃分,邊界設置:設置f[0] = 1後,f[1]~f[n]都能被計算出

  • lc 1218. 最長定差子序列:這題用DP四部曲分析,思路會很清晰,但在狀態表示時用f[i]f[i] 表示所有以第 i 個數結尾,公差爲 d 的子序列,複雜度 O(n2)O(n^2) 會超時,而狀態表示用 f[i]f[i] 表示以 i 結尾,公差爲 d 的所有子序列就可以優化時間複雜度,另外注意 i 可能是負數,所以要用 map

  • ac282. 石子合併 區間DP專題 核心:最後一次合併一定是左邊連續的一部分和右邊連續的一部分進行合併。
    狀態表示:f[i][j]f[i][j] 表示將 iijj 合併成一堆的方案的集合,屬性 Min。
    狀態計算:
    i<ji < j 時,f[i][j]=minikj1{f[i][k]+f[k+1][j]+s[j]s[i1]}f[i][j] = \min\limits_{i\leq k \leq {j - 1}}\{f[i][k]+f[k+1][j] + s[j] -s[i - 1]\}
    i=ji = j 時, f[i][i]=0f[i][i] = 0 (合併一堆石子代價爲 0)
    問題答案: f[1][n]f[1][n]
    所有的區間dp問題,第一維都是枚舉區間長度,一般 len = 1 用來初始化,枚舉從 len = 2 開始,第二維枚舉起點 i (右端點 j 自動獲得,j = i + len - 1)。其實也可以按左端點倒着枚舉,因爲只要保證狀態轉移所依賴的狀態被提前計算出來即可 參考這裏

  • lc 312. 戳氣球區間DP 狀態表示:f[i][j]f[i][j] 表示戳破 (i,j)(i,j) 之間所有氣球的集合,屬性(最大值)。這題的狀態表示很巧妙,用到了開區間。狀態計算:設 i 和 j 之間最後一個被戳破的氣球爲 k,那此時 (i,k)(i,k)(k,j)(k,j) 之間的氣球已被戳破,最後 (i,j)(i,j) 之間只剩下氣球 k ,相鄰的就是氣球 i 和 j
    ji>=2j - i >= 2時,f[i][j]=f[i][k]+f[k][j]+p[i]p[k]p[j]f[i][j] = f[i][k] + f[k][j] + p[i]*p[k]*p[j]
    狀態窮舉,最重要的一點是狀態轉移時用到的狀態都已被提前計算出來,這題含有圖解


  1. 狀態表示(由經驗所得):f[i][j]f[i][j]表示所有A[1,...,i]A[1,...,i]B[1,...,j]B[1,...,j]的公共子序列的集合,屬性(最大值)
  2. 狀態計算:劃分依據 a[i]a[i]b[j]b[j] 是否相同
    f[i][j]={f[i1][j1]+1if (a[i]==b[j])max(f[i1][j],f[i][j1])if (a[i]!=b[j]) f[i][j] = \left\{ \begin{aligned} &f[i-1][j-1] + 1,&\text{if } (a[i] == b[j]) \\ &max(f[i-1][j],f[i][j-1]),&\text{if } (a[i]!=b[j]) \end{aligned} \right.
    說明:上面的 a[i] 表示 A 的第 i 個數
  3. 邊界設置:只要設置 f[i][0]=0f[i][0] = 0f[0][j]=0f[0][j] = 0,則循環中所有狀態都可以計算
  4. 問題答案f[1][n]f[1][n] (由狀態表示的實際含義推得)
    說明:DP 問題求最大值的時候只要保證不遺漏就行了,可以重複,求最長公共子序列時,f[i1][j]f[i-1][j]包含00和01,而f[i][j1]f[i][j-1]包含00和10,所以他們是有交集的

  1. 狀態表示f[i][j]f[i][j]表示A[1,..,i]A[1,..,i]B[1,..,j]B[1,..,j]的公共子串,且以A[i]A[i]B[j]B[j]結尾的結合,屬性(最大值)

  2. 狀態計算:劃分依據 a[i]a[i]b[j]b[j] 是否相同
    f[i][j]={f[i1][j1]+1if (a[i]==b[j])0if (a[i]!=b[j]) f[i][j] = \left\{ \begin{aligned} &f[i-1][j-1] + 1,&\text{if } (a[i] == b[j]) \\ &0,&\text{if } (a[i]!=b[j]) \end{aligned} \right.

  3. 邊界設置:只要設置 f[i][0]=0f[i][0] = 0f[0][j]=0f[0][j] = 0,則循環中所有狀態都可以計算

  4. 問題答案max1in,1jmf[i][j]\mathop{max} \limits _{1\leq i \leq n,1\leq j \leq m}f[i][j] (由狀態表示的實際含義推得)

  • lc 813. 最大平均值和的分組:狀態表示: f[i][j]f[i][j]表示前 i 個元素分成 j 組的方案集合,屬性(最大值),狀態計算:把前面的 j - 1 組用一個狀態表示,但要注意有些集合不一定存在,畫一個分析圖會很清楚!

數學

  • lc204. 計數質數線性篩素數 核心:每個數只會被最小的質因數篩掉
  • lc372. 超級次方快速冪 a[1,5,2,6]=(a[1,5,2])10a6a^{[1,5,2,6]} = (a^{[1,5,2]})^{10} a^6 可以看到問題的規模減小了,所以可以用遞歸來解決
  • lc96. 不同的二叉搜索樹卡特蘭數 模版題
  • lc 829. 連續整數求和數學公式推導 起點 ii,長度 nn,求和公式(2i+n1)n/2=N(2*i+n-1)n/2 = N,解出 i=(Nn(n1)/2)/ni = (N-n(n-1)/2)/n。我們從11開始遍歷長度 nnii 一開始會很大),如果i<1N<n(n+1)/2i < 1即N < n(n+1)/2退出循環;如果 (Nn(n1)/2)%n==0(N - n * (n - 1)/2) \% n == 0 說明起點 ii 存在。時間複雜度: O(N)O(\sqrt N) 另一種思路:3 個連續整數(a,b,c)時,b 比 a 大 1,c 比 a 大 2,如果 N - 1 - 2 能整除 3,則商、商+1 與商+2 構成 N,其餘同理

分治

  • 劍指offer. 醜數三路歸併 {ai}\{a_i\} 表示至少包含一個因子 2 的醜數集合…,藉助 {ai/2},{bi/3},{ci/5}\{a_i / 2\},\{b_i/3\},\{c_i / 5\} 去還原 {ai},{bi},{ci}\{a_i \},\{b_i\},\{c_i \}。當我們先把 1 加到答案集合後,每當我需要用 aia_i 這個值時,可以發現在一步步把醜數添加到答案集合的過程中 ai/2a_i / 2 這個值一定已經被求出,所以乘 2 後就得到了 aia_i,同理可知 bjb_jckc_k 這樣就可以進行三路合併了。
  • lc 313. 超級醜數K路合併 和求第 n 個醜數類似,只不過那裏是三路合併,這裏是 K 路合併

編程競賽記錄

算法編程競賽的一些經驗和教訓

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章