Cheapest Palindrome(POJ-3280)

題意:給一個長度爲m的字符串s,其中含有n種字母,給出添加和刪除某種字母所需要的代價,你可以通過添加或刪除某些字母來使這個字符串變成迴文串,求使當前這個字符串變成迴文串需要的最少代價是多少。

題解:典型的區間DP,可以通過小區間一點一點的推到大區間。定義dp[i][j]表示把區間(i,j)編爲迴文串所需要的最小代價,那麼我們可以從以下幾個方面來分析。

首先對於一個區間dp[i][j]來說,如果s[i]==s[j],那麼對於這個區間來說,兩端的s[i]和s[j]加上以後不會對原區間dp[i+1][j-1]產生影響,使區間dp[i][j]成爲迴文串的最小代價就是使區間dp[i+1][j-1]的最小代價。此處狀態轉移方程:

dp[i][j]=dp[i+1][j-1];

其次,對於一個區間來講,它可以通過兩個位置推過來,就是它的子區間dp[i+1][j]和dp[i][j-1]。爲什麼是這兩個位置呢?因爲我們每次判斷多一個字母的位置才方便判斷,當判斷它之前,它之前的區間已經被處理過了,所以對於一個迴文串加上一個字母讓它變成新的迴文串有兩種操作,要麼是刪除掉這個字母,要麼是在這個字母所在位置的另一端加上相同的字母,所以只用比較是刪除當前字母的代價小還是添加一個新的代價小。所以此處的轉移方程是:

dp[i][j]=min(dp[i+1][j]+min(add(s[i]),del(s[i])),dp[i][j]);

dp[i][j]=min(dp[i][j-1]+min(add(s[i]),del(s[i])),dp[i][j]);

分析完狀態轉移方程,我們就可以枚舉區間長度和區間起點和終點,對每個區間進行計算,最後我們要求的值就是dp[0][slen-1]。

附上代碼:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=2010;
char s[maxn];
int dp[maxn][maxn];///dp[i][j]表示把區間[i,j]的字符串變成迴文串所需的最小花費
int num[maxn];
int main()
{
    int n,m,x,y;
    char c;
    scanf("%d%d",&n,&m);
    scanf("%s",s);
    for(int i=1;i<=n;i++)
        {
            cin>>c>>x>>y;
            num[c]=min(x,y);
        }
        memset(dp,0,sizeof(dp));
    int len=strlen(s);
    for(int l=1;l<=len;l++)//枚舉區間長度
    for(int i=0,j=l;j<len;i++,j++)//枚舉每一段的開頭和末尾
    {
         dp[i][j]=0x3f3f3f3f;
        if(s[i]==s[j])//頭和尾相等
        dp[i][j]=dp[i+1][j-1];//那就和上一段一樣
        else {//否則在左右兩個子區間裏進行狀態轉移
         dp[i][j]=min(dp[i+1][j]+num[s[i]],dp[i][j]);
        dp[i][j]=min(dp[i][j-1]+num[s[j]],dp[i][j]);
        }
    }
    printf("%d\n",dp[0][len-1]);
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章