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 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
Sample Input
3 4 abcb a 1000 1100 b 350 700 c 200 800
Sample Output
900
Hint
百度翻译:
跟踪所有的奶牛可能是一项棘手的任务,因此农夫约翰安装了一个自动化系统。他在每头奶牛身上都安装了一个电子ID标签,当奶牛经过扫描仪时,系统会读出这个标签。每个ID标签的内容目前是一个长度为m(1≤m≤2000)的字符串,字符由n(1≤n≤26)个不同符号(即小写罗马字母)组成。
奶牛是一种恶作剧的动物,有时会试图向后走来吓唬这个系统。而一头ID为“a bcba”的奶牛,无论走哪个方向,都会读到相同的信息,而一头ID为“abcb”的奶牛,可能会注册为两个不同的ID(“abcb”和“bcba”)。
FJ想要改变奶牛的ID标签,这样无论奶牛从哪个方向经过,它们的读数都是一样的。例如,可以通过在末尾添加“a”来更改“abcb”,以形成“abcba”,从而使ID为回文(前后读取相同)。将ID更改为回文的其他一些方法包括在开头添加三个字母“bcb”以生成ID“bcbabcb”,或者删除字母“a”以生成ID“bcb”。可以在字符串中的任何位置添加或删除字符,从而生成比原始字符串长或短的字符串。
不幸的是,由于ID标签是电子的,每个字符插入或删除都有一个成本(0≤成本≤10000),具体取决于要添加或删除的字符值。考虑到Cow ID标签的内容和插入或删除每个字母表字符的成本,找到更改ID标签的最低成本,以满足FJ的要求。一个空的ID标签被认为可以满足向前和向后读取相同内容的要求。只有具有相关成本的字母才能添加到字符串中。
思路:
典型的区间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]。
注意:对同一个字符添加和删除可以看作是同样的操作,所以先预处理,记下花费最少的操作。
1 #include <cstdio> 2 #include <fstream> 3 #include <algorithm> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <string> 9 #include <cstring> 10 #include <map> 11 #include <stack> 12 #include <set> 13 #include <sstream> 14 #include <iostream> 15 #define mod 1000000007 16 #define ll long long 17 using namespace std; 18 19 int n,m; 20 string str; 21 int dp[2500][2500]; 22 int add[200];//以字符的ASCII码作为坐标,所以要有200个空间 23 int main() 24 { 25 while(scanf("%d %d",&n,&m)!=EOF) 26 { 27 cin>>str; 28 char ch; 29 int x,y; 30 for(int i=0;i<n;i++) 31 { 32 cin>>ch>>x>>y; 33 add[ch]=min(x,y);//操作该字符的最少花费 34 } 35 memset(dp,0,sizeof(dp)); 36 for(int k=1;k<m;k++)//枚举区间长度 37 { 38 for(int i=0,j=k;j<m;j++,i++)//枚举每一段的开头和末尾 39 { 40 dp[i][j]=0x3f3f3f3f; 41 if(str[i]==str[j])//头和尾相等 42 { 43 dp[i][j]=dp[i+1][j-1];//那就和上一段一样 44 } 45 else//否则在左右两个子区间里进行状态转移 46 { 47 dp[i][j]=min(dp[i+1][j]+add[str[i]],dp[i][j]); 48 dp[i][j]=min(dp[i][j-1]+add[str[j]],dp[i][j]); 49 } 50 } 51 } 52 printf("%d ",dp[0][m-1]); 53 } 54 }