給你乙個只含小寫字母的字串,每次只能刪除一段含有一樣字母的區間,問最少刪多少次,才能刪除整個字串
(第一次做區間dp 所以記錄詳細點 適合新手)
我們用dp[ i ][ j ]代表把區間 i 到 j 完全刪除需要的次數
狀態轉移方程
if(s[ i ] == s[ j ]) dp[ i ][ j ] = dp[i+1][j-1]+1; (兩頭一樣,我們把中間的消去,只剩下兩頭了,再消一次就好了)
if(s[ i ] != s[ j ]) dp[ i ][ j ] =min(dp[ i+1 ][ j ],dp[ i ][ j-1 ])+1;(兩頭不一樣,左端單獨消去或者右端單獨消去)
列舉區間i,j中的點,dp[ i ][ j ] = min(dp[ i ][ j ],dp[ i ][ k ] + dp[ k ][ j ] - 1);
前兩個狀態呢,比較容易得到,但是這兩個狀態只解決了一半的問題。
我們先來看如何實現這兩個狀態,首先,我們做dp,根據dp陣列的意義,
所有 i > j 的dp值為0(區間在 i < x < j )
當i = j 時,dp[ i ][ j ]=1(區間內就乙個數)
如圖 5x5
而根據剛才初步推出來的狀態轉移方程,我們知道dp[ i ][ j ]要麼等於黃色格仔+1,要麼等於兩個藍色格仔最小值+1;
所以,我們根據已知條件和狀態轉移方程就可以把dp陣列填滿
當然 我們得要斜著填 如下圖 (因為每乙個dp值是由其左下方、左邊、下邊、的值得到的)
//斜著遍歷
for(int c=1;c好,那麼這是前兩個狀態轉移,看似解決了問題,但是還有一種特殊情況沒被解決;
比如: abaca
根據我們的狀態轉移方程2 第乙個和第五個字元一樣,所以dp[ 1 ][ 5 ] = dp[ 2 ][ 4 ] +1=4
但是其實答案是3(用兩步消掉 b c ,一步消除所有的a)
為了解決這種狀態,我們在區間i j內列舉每乙個點為k,dp[ i ][ j ] = min(dp[ i ][ j ],dp[ i ][ k ] + dp[ k ][ j ] - 1);
正常的話dp[ i ][ j ] 是等於 dp[ i ][ k ] + dp[ k ][ j ] - 1的如 abc 或 aba
但是當 特殊情況時 如 aaa 那麼dp[ i ][ k ] + dp[ k ][ j ] - 1得到的值是小於dp[ i ][ j ]的,才是答案。
#include#include#include#includeusing namespace std;
const int maxn=1e3+7;
int dp[maxn][maxn];
char s[maxn];
int n;
int main()}}
printf("%d\n",dp[1][n]);
return 0;
}
區間DP入門
區間dp,看名字其實會聯想到劃分dp,其實兩者的關係並不大。劃分dp是從頭到尾劃分解決,並且有劃分數量,而區間dp沒有這些限制條件,可以從任意區間開始,一直擴大到整個區間。不斷遞推求解。同樣也是分兩步去做。首先 還是進行資料處理,比如用陣列sum i j 去儲存i到j的和,或者是用別的方式處理並儲存...
區間DP入門
今天學長給我們講了區間dp,當然聽得雲裡霧裡,講完之後基本處於自閉狀態,然後還是自己到大佬的部落格,然後看部落格,但是並沒有找到很詳細的部落格,所以我想自己寫一寫,大神們勿噴哈.一 定義 區間dp,顧名思義是在區間上dp,它的主要思想就是先在小區間進行dp得到最優解,然後再利用小區間的最優解合併求大...
區間dp入門
區間dp就是區間上的dp,先算出小區間的最優解,再由小區間合併推出大區間的最優解。include include include includeusing namespace std const int inf 0x3f3f3f3f const int maxn 1010 int n int a m...