题意:有一个由n个小写字母组成的,长度为m的字符串,可以对其通过增加字符或者删除字符来使其变成回文。而增加或者删除字符都有一个花费,求解使该字符串变成回文的最小花费。
思路:DP。dp[i][j]表示串str[i~j]变成回文的最小代价,故状态转移方程为:
当str[i] == str[j]时 dp[i][j] = dp[i+1][j-1];
当str[i] != str[j]时 dp[i][j] = min( dp[i+1][j]+add[str[i]-'a'], dp[i][j-1]+add[str[j]-'a'],
其实dp很难逃出3种思路:
1、一维线性dp:每次考虑i时,选择最优子问题要么在i-1,要么在1...i-1里;
2、二维线性dp:考虑(i,j)子问题时,选择最优子问题要么在(i+1,j)、(i,j-1),要么在i<= k <=j,在k里;
3、树形dp:考虑i节点最优时,选择子节点最优,一般融合了01背包dp的双重dp。
上面3中模式也是我在做题后才发现的。
这个dp题其实就可以仿照第2中思路。
假设一个字符串Xx....yY;对于求这个字符串怎么求呢?
分4中情况讨论:
1、去掉X,取x....yY回文;
2、去掉Y,取Xx....y回文;
3、在左边加上X,取Xx....yYX回文;
4、在右边加上Y,取YXx....y回文。
至于去掉X、Y肯定没有第1、2中情况合算;加上X、Y肯定没有第3、4中情况合算。
因此令dp[i][j]为i...j要变成回文字符串的最小代价。
方程:
dp[i][j] = min{
其实分析发现,对于X而言,只要去 去掉 和加上 X 最小代价就行(因为前面dp串一样),Y同理。
因此最后得出:
dp[i][j] = min{
dp时候还有些注意事项:
比如当X和Y字符一样时,则在dp时必须先为x...y的最小代价。
源代码:(14056K, 79MS) #include#define min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int MAX = 2005; int dp[MAX][MAX]; int add[30], del[30]; int main(){ int n, m, i, j, len; char c, str[MAX]; cin >> n >> m >> str; for(i = 0; i < n; i ++){ cin >> c; cin >> add[c-'a'] >> del[c-'a']; } for(len = 1; len < m; len ++) for(i = 0; i + len < m; i ++){ j = i + len; if(str[i] == str[j]){ dp[i][j] = dp[i+1][j-1]; }else{ dp[i][j] = min( min(dp[i+1][j]+add[str[i]-'a'], dp[i][j-1]+add[str[j]-'a']), min(dp[i+1][j]+del[str[i]-'a'], dp[i][j-1]+del[str[j]-'a'])); } } cout << dp[0][m-1] << endl; return 0; }