區間DP思維起步

2021-08-04 07:57:36 字數 3668 閱讀 1670

**這裡

區間dp顧名思義就是在乙個區間上進行的一系列動態規劃。對一些經典的區間dp總結在這裡。

1) 石子歸併問題

描述:有n堆石子排成一排,每堆石子有一定的數量。現要將n堆石子並成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過n-1次合併後成為一堆。求出總的代價最小值。

分析:要求n個石子歸併,我們根據dp的思想劃分成子問題,先求出每兩個合併的最小代價,然後每三個的最小代價,依次知道n個。

定義狀態dp [ i ] [ j ]為從第i個石子到第j個石子的合併最小代價。

那麼dp [ i ] [ j ] = min(dp [ i ] [ k ] + dp [ k+1 ] [ j ])

那麼我們就可以從小到大依次列舉讓石子合併,直到所有的石子都合併。

這個問題可以用到平行四邊形優化,用乙個s【i】【j】=k 表示區間 i---j 從k點分開才是最優的,這樣的話我們就可以優化掉一層複雜度,變為o(n^2).

**:

[cpp]view plain

copy

print?

#include 

#include 

#include 

#define n 210

intdp[n][n],sum[n];  

intmain()  

memset(dp,0,sizeof

(dp));  

inti,j,l,k;  

for(l = 2; l <= n; ++l)  

}  }  printf("%d\n"

, dp[1][n]);  

}  return

0;  

}  

平行四邊形優化**:

[cpp]view plain

copy

print?

#include 

#include 

#include 

#define n 210

intdp[n][n],sum[n],s[n][n];  

intmain()  

memset(dp,0,sizeof

(dp));  

inti,j,l,k;  

for(l = 2; l <= n; ++l)  

}  }  }  

printf("%d\n"

, dp[1][n]);  

}  return

0;  

}  

2)括號匹配

poj2955,

nyoj 15

描述:給出一串的只有『(』 『)』  '[『  ']'四種括號組成的串,讓你求解需要最少新增括號數讓串中的所有括號完全匹配。

分析:我們求出這個串的最大匹配,然後串的總長度-最大匹配就是答案。

方法1:首先能想到的是轉化成lcs(最長公共子串行),列舉中間點,求所有的lcs中的最大值 * 2就是最大匹配。但是複雜度較高,光lcs一次就o(n^2)的複雜度。

方法2:

首先考慮怎麼樣定義dp讓它滿足具有通過子結構來求解、

定義dp [ i ] [ j ] 為串中第 i 個到第 j 個括號的最大匹配數目

那麼我們假如知道了 i 到 j 區間的最大匹配,那麼i+1到 j+1區間的是不是就可以很簡單的得到。

那麼 假如第 i 個和第 j 個是一對匹配的括號那麼dp [ i ] [ j ] = dp [ i+1 ] [ j-1 ] + 2 ;

那麼我們只需要從小到大列舉所有 i 和 j 中間的括號數目,然後滿足匹配就用上面式子dp,然後每次更新dp [ i ] [ j ]為最大值即可。

更新最大值的方法是列舉 i 和 j 的中間值,然後讓dp[ i ] [ j ] = max ( dp [ i ] [ j ] , dp [ i ] [ f ] + dp [ f+1 ] [ j ] ) ;

如果要求列印路徑,即輸出匹配後的括號。見詳細講解

**:

[cpp]view plain

copy

print?

#include 

#include 

#include 

#include 

using

namespace

std;  

const

intn = 120;  

intdp[n][n];  

intmain()  

}  cout<}  

return

0;  

}  

3)整數劃分問題

題目描述:給出兩個整數 n , m ,要求在 n 中加入m - 1 個乘號,將n分成m段,求出這m段的最大乘積

分析:根據區間dp的思想,我們定義dp [ i ] [ j ]為從開始到 i 中加入 j 個乘號得到的最大值。

那麼我們可以依次計算加入1----m-1個乘號的結果

而每次放入x個乘號的最大值只需列舉第x個乘號的放的位置即可

dp [ i ] [ j ]  = max (dp [ i ] [ j ] , dp [ k ] [ j-1 ] * a [ k+1 ] [ i ] ) ;

**:

[cpp]view plain

copy

print?

#include 

#include 

#define max(a,b) a>b?a:b

long

long

a[20][20];  

long

long

dp[25][25];  

intmain()  

}  if(ok==0&&l-1==m||l-1

long

long

x,ans;  

memset(dp,0,sizeof

(dp));  

for(

inti=0;i

dp[i][1]=a[1][i];  

ans=0;  

if(m==1)  

ans=dp[l-1][1];  

for(

intj=2;j<=m;j++)  

}  }  printf("%lld\n"

,dp[l-1][m]);  

}  return

0;  

}  

線性dp 區間dp

1 尼克的任務 額一道挺水的題,愣是做了幾個小時 動態規劃大致的思路還是找乙個轉移 換個詞就是影響 我們可以明顯看出本題的規則 空暇時,一遇到任務必須挑乙個接 求1 n時間內最大空暇時間 所以將任務排序是必要的,兩個關鍵字 再來想象一下當我做到第i 個任務時,我在 st i st i t i 1 時...

線狀DP及區間DP

這裡我們都用到動態規劃的思想 dynamic programming,簡稱dp。本質就是組合子問題來求解原問題,且對每個子問題只求解一次。一般來說四個步驟 1.刻畫乙個最優結構特徵 2.遞迴的定義最優解值 3.計算最優解的值 4.利用計算出的資訊構造乙個最優解 這邊直接給出 include incl...

區間dp小結

區間dp,顧名思義,就是在區間上dp,即把整個區間劃分為乙個個的小區間,在小區間內dp求出最優值,然後把這些小區間合併以後就是整個取件的最優值。下面是一些比較經典的區間dp題目 1.nyoj 737 石子合併 題意 有n堆石子,每堆有a i 個,每次合併時只能合併相鄰的兩堆,代價為兩堆石子的個數之和...