1. 程式人生 > >POJ 3280 Cheapest Palindrome ( 區間DP && 經典模型 )

POJ 3280 Cheapest Palindrome ( 區間DP && 經典模型 )

ide aps class nbsp 圖片 pri 子串 microsoft using

題意 : 給出一個由 n 中字母組成的長度為 m 的串,給出 n 種字母添加和刪除花費的代價,求讓給出的串變成回文串的代價。

分析 :

原始模型 ==> 題意和本題差不多,有添和刪但是並無代價之分,要求最少操作幾次才能是其變成回文串

① 其實細想之後就會發現如果沒有代價之分的話刪除和增添其實是一樣的,那麽除了原串原本擁有的最長回文串不用進行考慮處理,其他都需要進行刪除或者增添來使得原串變成回文串,所以只要對原串 S 和其反向串 S‘ 找出兩者的最長公共子串長度 L 再用總長減去 L 即可

② 也可以用 區間DP 思想進行求解

定義 dp[ i ][ j ] 為區間 [ i, j ] 的最少操作次數,初始化 dp[ i ][ i ] = 0

如果 str[ i ] == str[ j ],則當前區間 [ i, j ] 的數字可從區間 [ i+1, j-1 ] 轉移而來,即 dp[i][j] = dp[i+1][j-1]

如果 str[ i ] ! = str[ j ],現只考慮刪除操作那麽必定要刪除左右邊界的一個故 dp[i][j] = min( dp[i+1][j], dp[i][j-1] ) + 1

現在看回這題,這題對具體的添加和刪除操作都附上了相應的權值,其實只要對上述解法②稍加修改即可

dp[ i ][ j ] 和上面的定義一樣,初始化 dp[ i ][ i ] = 0

如果 str[ i ] == str[ j ], 依舊是 dp[i][j] = dp[i+1][j-1]

如果 str[ i ] ! = str[ j ]

此時還是從 dp[i+1][j] 和 dp[i][j-1] 轉移到 dp[i][j]

由於有增刪區別存在,自然想到取最小,可得下面的轉移方程

dp[i][j] = min( dp[i+1][j]+min( delete or add str[i] ) , dp[i][j-1]+min( delete or add str[j] ) )

具體編程的時候枚舉區間長度為 dp 的階段、然後再枚舉當前長度下的每一個子串

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10
; int dp[maxn][maxn], ADD[30], DEL[30]; char str[maxn]; int main(void) { int N, M; while(~scanf("%d %d %s", &N, &M, str)){ memset(ADD, 0, sizeof(ADD)); memset(DEL, 0, sizeof(DEL)); char ch; while(N--){ scanf(" %c", &ch); scanf("%d %d", &ADD[ch-a], &DEL[ch-a]); //printf("%c %d %d\n", ch, ADD[ch-‘a‘], DEL[ch-‘a‘]); } for(int i=1; i<=M; i++) dp[i][i] = 0; for(int i=1; i<M; i++) for(int j=0; j+i<M; j++) if(str[j] == str[j+i]) dp[j][j+i] = dp[j+1][j+i-1]; else{ int temp1 = dp[j+1][j+i] + min(ADD[str[j]-a], DEL[str[j]-a]); int temp2 = dp[j][j+i-1] + min(ADD[str[j+i]-a], DEL[str[j+i]-a]); dp[j][j+i] = min(temp1, temp2); } printf("%d\n", dp[0][M-1]); } return 0; }
View Code

為什麽這樣的轉移是對的?貌似還是太抽象,下面用樣例來跑一下這個 DP

N = 3、M = 4
abcb
a 1000 1100
b 350 700
c 200 800

根據程序有以下結果

長度 == 1

dp[0][1] => a != b => 添 b 代價較小 => dp[0][1] = 350 意義為 "bab" ( 添b了)

dp[1][2] => b != c => 添 c => dp[1][2] = 200 表示 "cbc"

dp[2][3] => c != b => 添 c => dp[2][3] = 200 表示 "cbc"

長度 == 2

dp[0][2] => a != c => 添 c => dp[0][1] + 200 = 550 表示 "cbabc" ( 註意 dp[0][1] 已經變了 )

dp[1][3] => b == b => dp[2][2] = 0 表示 "bcb"

長度 == 3

dp[0][3] => a != b => 添 b => dp[0][2] + 350 = 900 表示 "bcbabcb"

上述的 添xx 其實是經過了四個值的比較,看程序就知道。

比如最後的 dp[0][3] 實際是比添 a 更加優,如果添 a 的話那麽就變成 dp[1][3] + 1000 = 1000 意義是 "abcba"

POJ 3280 Cheapest Palindrome ( 區間DP && 經典模型 )