题意:给出一个由m中字母组成的长度为n的串,给出m种字母添加和删除花费的代价,求让给出的串变成回文串的代价。
分析:我们知道求添加最少的字母让其回文是经典dp问题,转化成LCS求解。这个是一个很明显的区间dp
我们定义dp [ i ] [ j ] 为区间 i 到 j 变成回文的最小代价。
那么对于dp【i】【j】有三种情况
首先:对于一个串如果s【i】==s【j】,那么dp【i】【j】=dp【i+1】【j-1】
其次:如果dp【i+1】【j】是回文串,那么dp【i】【j】=dp【i+1】【j】+min(add【i】,del【i】);
最后,如果dp【i】【j-1】是回文串,那么dp【i】【j】=dp【i】【j-1】 + min(add【j】,del【j】);
dp过程中的细节及递推顺序非常重要,一开始我直接两重循环dp[i][j],i取0到len-1,j取i+1到len-1,
dp[0][0],dp[0][1],dp[0][2],dp[0][3],...dp[0][len-1]
...
dp[len-1][len-1]
这样子是wrong的,因为,比如说,求dp[0][2]的时候,由于dp[0][1]和dp[1][2]的正确值都还没有求出来,所有导致dp[0][2]的结果
错误,正确的做法应该以区间的长度为单位循环,先循环长度为1的,再循环为2到,...
这样才确保每一步正确
还有个细节就是,在循环外dp初始化0,在循环内部dp[i][j]先置为INF,再比较出最优解
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <cmath>
#define maxn 2005
#define maxz 2005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int n,len;
string s;
char ch;
int dp[maxn][maxn];
map<char,int>add,del;
void solve()
{
memset(dp,0,sizeof(dp));
for(int k=0;k<len;k++)
{
for(int i=0,j=i+k;j<len;j++,i++)
{
if(i==j)
{
dp[i][j]=0;
continue;
}
dp[i][j]=INF;
if(s[i]==s[j])
{
dp[i][j]=dp[i+1][j-1];
}
// if(dp[i][j-1]==0)
// {
//dp[i][j]=min(dp[i][j],dp[i][j-1]+min(add[s[j]],del[s[j]]));
// }
//if(dp[i+1][j]==0)
//{
//dp[i][j]=min(dp[i][j],dp[i+1][j]+min(add[s[i]],del[s[i]]));
//}
//if(dp[i][j-1]&&dp[i+1][j])
//{
dp[i][j]=min(dp[i][j],min(dp[i][j-1]+min(add[s[j]],del[s[j]]),dp[i+1][j]+min(add[s[i]],del[s[i]])));
//}
}
}
/*for(int i=0;i<len;i++)
{
for(int j=i;j<len;j++)
cout << dp[i][j] << ' ';
cout << endl;
}*/
cout << dp[0][len-1] << endl;
}
int main()
{
//ios::sync_with_stdio(false);
while(cin>>n>>len)
{
cin>>s;
int ac,dc;
add.clear();
del.clear();
for(int i=0;i<n;i++)
{
cin>>ch>>ac>>dc;
add[ch]=ac;
del[ch]=dc;
}
solve();
}
return 0;
}