區間DP值入門題目 石子歸併

2021-09-16 21:21:09 字數 3013 閱讀 2617

這兩天特別喪,不想學習,心態有點崩。不知道為什麼,可能是假期綜合症吧,又感覺自己處在了一種極度的迷茫之中,都怪袁老,讓我自己一天吃飽了沒事幹,胡思亂想,嗚嗚嗚.jpg。

區間dp

區間dp就是在區間上的動態規劃,求解一段區間上的最優解,通過合併小區間的最優解來得到整個大區間上的最優解的演算法。

時間複雜度一般為o(n^2)或者o(n^3),一般做法就是:

確定狀態,例如初始化等操作。

列舉區間長度,列舉區間起始點,有的題目還需要列舉區間斷點,從小區間到大區間,合併小區間來得到整個大區間上的最優解。

dp[1][n]一般就是所求的解。

石子合併問題

很多人一看到石子合併問題可能會想到博弈論,但是博弈論上的一般是先手問題。而石子合併合併的一般是合併相鄰兩堆石子的數量。石子合併問題有兩種玩法。操場玩法和路邊玩法,操場玩法就是指把石子圍成一堆,路邊玩法就是把石子擺成一條直線。

貪心法不能解決,貪心法要求無後效性,但是條件為必須合併相鄰兩堆這個條件,貪心法無法保證每次都能取到所有堆中石子數最少的兩堆。如6                 3  4  6  5  4  2貪心法的最小值是62,但是實際上最小值為61,很明顯貪心算出來的並不是最小值,貪心法在子過程中得到的解只是區域性最優,而不能保證全域性的值最優,因此本問題不可以使用貪心法求解。如果使用暴力窮舉的辦法,會有大量的子問題重複,這種做法會造成效率的低下,所以我們可以考慮動態規劃法,是否具有最優子結構性質。

(1)分析最優解的結構特徵。

假設已經知道了第k堆石子分開可以得到最優解,那麼原問題就變成了兩個子問題,子問題分別是和原問題的最優解是否包含子問題的最優解呢?

假設已經知道了n堆石子合併起來的花費是c,子問題1和石子合併起來的花費是b,石子數量之和是w(i,j)那麼c=a+b+w(i,j).因此我們只需要證明c是最優的,則a和b一定是最優的(即原問題的最優解包含子問題的最優解)。證明用反證法來證。

(2)建立最優值遞迴式。

設dp[i][j]代表從第i堆到第j堆石子合併的最小花費,dp[i][k]代表從第i堆石子到第k堆石子合併的最小花費,dp[k+1][j]代表從第k+1堆石子到第j堆石子合併的最小花費。w(i,j)代表從第i堆到第j堆的石子數量之和。

dp[i][j]=0           i=j              //        min(dp[i][k]+dp[k+1][j]+w(i,j)   i題目

51nod - 1021 

n堆石子擺成一條線。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的代價。計算將n堆石子合併成一堆的最小代價。

例如: 1 2 3 4,有不少合併方法

1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)

1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)

1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

括號裡面為總代價可以看出,第一種方法的代價最低,現在給出n堆石子的數量,計算最小合併代價。

input

第1行:n(2 <= n <= 100)

第2 - n + 1:n堆石子的數量(1 <= a[i] <= 10000)

output

輸出最小合併代價

sample input

412

34

sample output

19
#include#include#include#includeusing namespace std;

const int inf=1<<30;

const int maxn=40001;

int min[201][201];

//int max[maxn][maxn];

int sum[maxn];

int a[maxn];

int main()

for(int i=1;i<=n;i++)

//c=a+b+w(i,j);w(i,j)代表價值和

for(int v=2;v<=n;v++)

{for(int i=1;i<=n-v+1;i++)

{int j=i+v-1;

min[i][j]=inf;

//max[i][j]=-1;

int tmp=sum[j]-sum[i-1];

for(int k=i;k這就是區間dp石子問題的常規解法,如果石子長度不長的話一般直接用dp來做,複雜度為o(n ^ 3),  但是這種方法時間複雜度並不低,在對時間有明顯要求的時候這種方法不適合使用,這時候就要用到了一中新演算法,叫(garsiawachs演算法) 能把複雜度降到o(n ^ 2)甚至o(nlogn);

它的步驟如下:

設序列是stone,從左往右,找乙個滿足stone[k-1] <= stone[k+1]的k,找到後合併stone[k]和stone[k-1],再從當前位置開始向左找最大的j,使其滿足stone[j] > stone[k]+stone[k-1],插到j的後面就行。一直重複,直到只剩下一堆石子就可以了。在這個過程中,可以假設stone[-1]和stone[n]是正無窮的。

舉個例子:

假設石子的序列為100,45, 67, 200, 78; 那麼第一次找的時候找到了67, 因為200 > 45,  然後合併為 112, 往前遍歷,插到第乙個比它大的值後面即stone[-1], 然後變成了112, 100, 200, 78,  繼續操作, 然後合併112, 100為 212, 序列變為212 200 78,  記得stone[n]為無窮哦, 然後合併200, 78為278, 變為278, 212, 最後在合併為490

所以結果為112 + 212 + 278 + 490 = 1092。這種演算法我還不是很會,嗚嗚嗚,等我學會了再說。

圓型石子合併經常轉換為直線型來求,也就是說,把圓形結構看成是長度為原規模兩倍的直線結構來處理。如果操場玩法(直線玩法)原問題規模為n,所以就相當於有一排石子a1,a2,……,an-1.該問題規模為2*n-1,然後就可以用線性的石子合併問題來解決。

石子歸併 區間DP

n堆石子擺成一條線。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的代價。計算將n堆石子合併成一堆的最小代價。例如 1 2 3 4,有不少合併方法 1 2 3 4 3 3 4 3 6 4 9 10 19 1 2 3 4 1 5 4 5 1 9...

石子歸併(區間DP)

測評傳送門 題目描述 有n堆石子排成一列,每堆石子有乙個重量w i 每次合併可以合併相鄰的兩堆石子,一次合併的代價為兩堆石子的重量和w i w i 1 問安排怎樣的合併順序,能夠使得總合併代價達到最小。輸入描述 第一行乙個整數n n 100 第二行n個整數w1,w2.wn wi 100 輸出描述 乙...

區間DP入門之 石子歸併問題

有n堆石子排成一排,每堆石子有一定的數量。現要將n堆石子並成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過n 1次合併後成為一堆。求出總的代價最小值。輸入 有多組測試資料,輸入到檔案結束。每組測試資料第一行有乙個整數n,表示有n堆石子。接下來的一行有n ...