最小M段和 O nlogn 快速演算法

2021-05-25 12:08:43 字數 2418 閱讀 9949

先宣告,這演算法我還沒弄出來

《數列分割》解題報告

[題目描述]

給出乙個長為n的整數數列(n≤15000),要求求出最小的m,使得存在一種將數列分成恰好k份的方案(每份是數列上連續的一段,且不得為空),每份的數字之和不大於m。

[演算法分析]

此題又是典型的「要求最大值盡量小」的形式,對於這樣的問題,往往可以使用基於二分的演算法解決。對於此題,則是二分列舉m的值,再判斷對於這個m是否存在劃分方案並縮小範圍。

此題的難度在於,數列中可能存在負數,那麼當數列的乙個字首的數字和已經大於m時,更後面的字首和仍然有可能小於m(因為負數)。另外,對於乙個特定的m,如果數列存在劃分為k-1份的方案,並不一定存在劃分為k份的方案,例如數列-1 -1 -2,對於m=-2,存在劃分為2份的方案,卻不存在劃分為3份的方案。這樣,一些常用的簡單演算法將不再適用。

在找不到有效演算法時,先來看看樸素的演算法。對於「是否存在一種恰好k份的劃分方案,使得每乙份的數字和不大於m」這一問題,很容易設計出這樣乙個動態規劃演算法:

令s[i]表示原數列前i個數構成的子串行,sum[i]表示s[i]中所有數字之和。定義f[i][j]表示是否存在將s[i]劃分為恰好j份的方案,其中每乙份的數字之和不大於m,f[i][j]都為true或false。

對於j=0,f[i][j]當且僅當i=0時為true,其餘為false。

對於i=0,f[i][j]當且僅當j=0時為true,其餘為false。

對於j>0且i>0,f[i][j]=f[i1][j-1] or f[i2][j-1] or … or f[il][j-1],ix

是所有在[0,i-1]範圍內且滿足sum[i]-sum[ix]≤m的整數。

這個演算法的時間複雜度高達o(n3

),對於本題資料範圍無法承受。但是容易發現,這個模型中狀態形式簡單(布林型別),轉移方程也比較特殊,可能會存在一些內在的規律。那麼現在我們來深入研究這個動態規劃模型。

轉移方程的第乙個特殊之處在於,對於同樣的i不同的j,方程中的i1…il

的值都是相同的

正在處理的是i=7一行,對於這一行中的每個格仔(狀態),它的值由前面行中和它同色格仔的值進行or運算得到,由於轉移方程的特殊形式,就成了上圖那樣的形態。如果將每一行看成乙個布林向量,那麼計算第i行實際上就是先找出i1…il

,再將這l個向量相加(此處是or運算),並向右位移一位,最高位捨棄(實際上一定為false),最低位補0,就得到了第i個向量。

不過這一認識暫時不能幫助降低演算法的時間複雜度,還需要更多的東西。回到轉移方程中,觀察i1…il

這個集合,它看似是散亂的,其實不然,它是ix

且sum[i]-sum[ix]≤m的集合,即sum[i]-m≤sum[ix],如果將0~i-1行按照sum值從大到小排序,那麼i1…il

對應的實際上就是前l行

那麼進行到第i行時,可以在0~i-1行使用二分查詢找出轉移所涉及的行,將他們or起來、右位移得到第i行的結果,再將第i行插入到合適的位置,保持0~i行按sum降序排列。

上面的描述涉及的操作有:二分查詢乙個位置、將元素(布林向量)插入到某個空的位置、求某個位置前所有向量or起來的值。這是典型的可以使用離散化+樹狀陣列解決的問題,但是,由於操作的元素是向量,基本操作就有o(n)的代價,導致演算法的時間複雜度為o(n2 log n),空間複雜度為o(n2

),對於本題的資料範圍無法承受。

回到前面的演算法分析,對於第i行,它的值是通過前面連續的l行or起來再進行一次位移得到的,如果將得到的值和前面l行再or起來,相當於把乙個向量右位移乙個單位再和自己or起來,給人直觀的感覺是,向量裡的true不會變得更「零散」,而有可能變得更「連續」,特別地,如果這個向量裡的true本來就是連續的一段,右位移再or的結果仍然是連續的。另外,第一行是連續的(只有1個true),這啟發我們可以使用數學歸納法來得到一些東西。稍作分析就能得到,在演算法進行的任意時刻,任意乙個向量裡的true都是連續的,且按sum值倒排後前任意多個連續向量or起來的結果裡的true也是連續的。

有了這個結論,我們就可以使用乙個整數對來描述乙個向量:(x,y)表示向量中從x到y這一段為true。or運算和位移運算都可以簡單地實現:

(x1,y1)or(x2,y2)=(min(x1,x2),max(y1,y2))

(x,y)>>1=(x+1,y+1)

將這樣的表示法用到前乙個演算法中,就得到了時間複雜度o(n log n)、空間複雜度o(n)的可行演算法。

[總結]

本題具有這樣低複雜度的演算法的關鍵原因在於,每個向量裡的true都是連續的,跳出上文的語境,就得到了乙個數學命題:

要求將乙個數列劃分為每份數字之和不大於m的若干份,如果同時存在劃分為a份和b份的方案,那麼也存在劃分為[a,b]中的每乙個份數的方案。

上文實際上證明了這個結論,但是通過了樸素的動態規劃演算法搭橋。慚愧的是我並沒有找到使用純數學工具進行證明的方法,希望各位大牛不吝賜教。

感謝xqz提供的程式

馬上我就自己打一邊然後發出來,不會讓大家看這麼醜的程式的......

最小m段和問題

description 給定n個整數組成的序列,現在要求將序列分割為m段,每段子序列中的數在原序列中連續排列。如何分割才能使這m段子序列的和的最大值達到最小?程式設計任務 給定n個整數組成的序列,程式設計計算該序列的最優m段分割,使m段子序列的和的最大值達到最小。input 輸入的第一行有2個正整數...

最小m子段和(動態規劃)

問題描述 給定n個整數組成的序列,現在要求將序列分割為m段,每段子序列中的數在原序列中連續排列。如何分割才能使這m段子序列的和的最大值達到最小?輸入格式 第一行給出n,m,表示有n個數分成m段,隨後一行給出n個數,以空格分隔 輸入樣例 9 39 8 7 6 5 4 3 2 1 輸出樣例 解釋 9 8...

codevs 3278 最小m 段和問題

時間限制 1 s 空間限制 256000 kb 題目等級 gold 題目描述 description 給定 n 個整數 不一定是正整數 組成的序列,現在要求將序列分割為 m 段,每段子序列中的數在原序列 中連續排列。如何分割才能使這 m 段子序列的和的最大值達到最小?輸入描述 input descr...