1. 程式人生 > >DP E - Cheapest Palindrome

DP E - Cheapest Palindrome

Keeping track of all the cows can be a tricky task so Farmer John has installed a system to automate it. He has installed on each cow an electronic ID tag that the system will read as the cows pass by a scanner. Each ID tag's contents are currently a single string with length M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N

(1 ≤ N ≤ 26) different symbols (namely, the lower-case roman alphabet).

Cows, being the mischievous creatures they are, sometimes try to spoof the system by walking backwards. While a cow whose ID is "abcba" would read the same no matter which direction the she walks, a cow with the ID "abcb" can potentially register as two different IDs ("abcb" and "bcba").

FJ would like to change the cows's ID tags so they read the same no matter which direction the cow walks by. For example, "abcb" can be changed by adding "a" at the end to form "abcba" so that the ID is palindromic (reads the same forwards and backwards). Some other ways to change the ID to be palindromic are include adding the three letters "bcb" to the begining to yield the ID "bcbabcb" or removing the letter "a" to yield the ID "bcb". One can add or remove characters at any location in the string yielding a string longer or shorter than the original string.

Unfortunately as the ID tags are electronic, each character insertion or deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending on exactly which character value to be added or deleted. Given the content of a cow's ID tag and the cost of inserting or deleting each of the alphabet's characters, find the minimum cost to change the ID tag so it satisfies FJ's requirements. An empty ID tag is considered to satisfy the requirements of reading the same forward and backward. Only letters with associated costs can be added to a string.

Input

Line 1: Two space-separated integers: N and M
Line 2: This line contains exactly M characters which constitute the initial ID string
Lines 3.. N+2: Each line contains three space-separated entities: a character of the input alphabet and two integers which are respectively the cost of adding and deleting that character.

Output

Line 1: A single line with a single integer that is the minimum cost to change the given name tag.

Sample Input

3 4
abcb
a 1000 1100
b 350 700
c 200 800

Sample Output

900

Hint

If we insert an "a" on the end to get "abcba", the cost would be 1000. If we delete the "a" on the beginning to get "bcb", the cost would be 1100. If we insert "bcb" at the begining of the string, the cost would be 350 + 200 + 350 = 900, which is the minimum.     分析:(別人的)
這題真想了很久,感覺無從下手。很容易知道可以利用動態規劃解決,但就連新增或刪除某字元這個操作都是很難的,更不要談該如何新增或刪除字串使得成為一個迴文字串。但其實根本不需要實際操作這些。。。我們可以仔細分析一下回文字串的特性,比如一個字串"a"和字串"b"的費用都是0因為它們已經迴文,而字串"ab"的費用是新增/刪除'a'或'b'產生的費用。我們並不需要實際操作,甚至不需要知道是刪除還是新增,因為可以發現對於一個已經是迴文的字串來說,在其前面或後面加一個字元會出現兩種情況:1.還是迴文字串,此時第一個字元和最後一個字元相等。2.變為非迴文字串,這樣就需要再在另一邊新增同樣字元或者刪除這個字元以此維持迴文,而到底選擇哪種只取決於是新增這個字元的費用小還是刪除這個字元的費用小。也就是說,我不需要實際操作,只是知道進行了一種操作,然後就能維持迴文。所以我們可以由內往外推,從一個字元開始,直到推向整個字串,推到最後也就是說所給的字串已經變為迴文字串的最小費用求出。設dp[i][j]表示i~j區間的字串變為迴文字串所產生的最小費用。由於是從內往外遞推的,所以對於上述的情況2來說轉移方程為:dp[i][j]
 = min(dp[i+1][j],dp[i][j-1]); 情況1的轉移方程是有所不同的,因為它此時不需要進行新增/刪除操作,而它的最小費用應該由區間(i-1)~(j-1)推過來(比如"aba",區間0~2是由區間1~1推來的,根本不需要新增/刪除'a'的費用,假如從0~1推來,0~1已經是含新增/刪除‘a'的費用了的,這樣肯定最終費用不會最小),轉移方程:dp[i][j] = min(dp[i][j],dp[i+1][j-1]); 也可以直接寫dp[i][j] = dp[i+1][j-1]; 注意當i ==
 j 和i+1 == j的時候i+1>j-1,所以這個要特判一下。   分析: 這題看了別人的題解,一開始並沒有看懂,後來把樣例帶進去算就差不多弄懂了,和上面的差不多。拿樣例來說吧,就是已知m(這個字串的長度),求這個字串變成迴文序列的最小花費的錢。就可以劃分成很多子問題從而得到狀態轉移方程。 子問題:不斷分割,先求長度為1,就是一個字元的迴文序列,也就是從i到i,顯而易見花費為0,然後利用程式碼中的k值來表示字串的長度,當k為1時,有很多狀態,例如0...1,1...2,2...3  內層迴圈將這裡狀態變成迴文序列進行求解最小花費。迴圈完畢,k++; 一直到求解出dp[0][m-1];(即是從0到m-1,長度為m,的迴文序列,即題目要求的)  
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
char s[2200];
int dp[2000][2000];
int cost[2200];
int main()
{
    int n,m;
    cin>>n>>m;
    scanf("%s",s);
    char x;
    int y=0,z=0;
    for(int i=0;i<n;i++)
    {
        cin>>x>>y>>z;
        cost[x-'a']=min(y,z);
    }
    for(int k=0;k<m;k++)//k代表的是i與j之間的距離
    {
        for(int i=0;i+k<m;i++)
        {
            int j=i+k;
            dp[i][j]=min(dp[i][j-1]+cost[s[j]-'a'],dp[i+1][j]+cost[s[i]-'a']);
            if(s[i]==s[j])
            {
                if(i==j||i+1==j) dp[i][j]=0;
                else dp[i][j]=dp[i+1][j-1];
            }
        }
    }
    cout<<dp[0][m-1]<<endl;
    return 0;
}