トピックリンク:3280-最も安い回文
質問の意味:文字列を入力してから、各文字を追加または削除するコストを指定します。元のシーケンスを回文に変換するための最小コストはいくらですか。
入力:最初の行は、シーケンスに含まれるさまざまな文字nとシーケンスの長さlを示します
次の行はシーケンスを示します
次のn行は、それぞれ各文字を追加または削除するための最小コストを示しています
分析:これは区間DPによって解決される問題です。f[i] [j]が、元のシーケンスのi番目の文字をj番目の文字に回文シーケンスに変更する最小コストを表すとします。 ]] [i] = 0、単一の文字も回文であるため、重要なのは動的転送の実行方法を確認することです
議論する2つの状況があります:
(1)s [i] == s [j]の場合、f [i] [j] = f [i + 1] [j-1]があります。これは、i番目とj番目の文字が等しいという意味であるためです。 i〜j番目のシーケンスを回文に変換するには、i + 1〜j-1を回文に変換するだけで済み、コストは同じです。
(2)s [i]!= s [j]、このとき、分類についてもう一度議論する必要があります。私たちが知ることができるのは、i〜jは、現時点では回文配列であってはならないということです。回文配列には、次の方法があります。j番目の文字の後にi番目の文字と同じ文字を追加するか、i番目の文字を直接削除するか、jと同じ文字を追加します。 iの前の-番目の文字、または直接削除するj番目の文字、1つずつ分析します
1. j番目の文字の後にi番目の文字と同じ文字を追加します。このとき、f [i] [j] = f [i + 1] [j]+iを追加するコスト-番目の文字、今回はデフォルトで、i + 1〜j番目の文字はすでに回文文字列です。
2. i番目の文字を直接削除します。このとき、f [i] [j] = f [i + 1] [j] + i番目の文字を削除するコストです。このとき、デフォルトでi+も使用します。 1〜jthキャラクターはすでに回文です
3. iの前にj番目の文字と同じ文字を追加します。このとき、f [i] [j] = f [i] [j-1]+j番目の文字を追加するコスト。この場合、デフォルトは最初のi〜j-1文字が回文です
4. j番目の文字を直接削除します。このとき、f [i] [j] = f [i] [j-1] + j番目の文字を削除するコスト。この場合もデフォルトのi〜j-1番目の文字です。 。は回文です
上記の場合は最小値を取るだけでよく、ケース1と2の両方でf [i + 1] [j]が使用されていることがわかります。唯一の違いは、i番目の文字を追加するかi番目の文字を削除するかです。文字。文字なので、読み込み時にi番目の文字を変更する最小コストを直接保存できます。つまり、加算と削除の間の最小値を取ります。これは、ケース3と4にも当てはまります。
初期化について話しましょう。最小値を見つけるために、すべてのf [i] [j]を正の無限大に初期化してから、f [i] [i] = 0、f[などの特別な値を変更する必要があります。 i] [i-1] = 0、f [i] [i]が0の場合、1つの文字が回文であり、まったく変更する必要がないため、コストは0ですが、fは理解しやすいです。 [i] [i-1]は0に等しいそれを理解する方法は?実際、これが無効な文字であることを見つけるのは難しくありません。つまり、最初の回答の更新でのみ機能します。つまり、長さ2の間隔の更新でのみ機能します。遷移方程式、s [i] == s [j]の場合、f [i] [j] = f [i + 1] [j-1]が存在するため、j = i + 1の場合、f [i] [j] = f [i + 1] [i]、実際にはこれは0に等しいはずなので、f[i][i-1]を0に初期化する必要があります。
コードは次のとおりです。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=2e3+10;
int f[N][N];//f[i][j]代表将原序列中第i个字符到第j个字符变为回文序列的最小代价
int mp[N];//mp[a]代表删除a和添加a所需代价的较小值
int main()
{
int n,l;
cin>>n>>l;
string s;
cin>>s;
s=" "+s;//让s的有效字符从1开始
memset(f,0x3f,sizeof f);
char op[5];
for(int i=1;i<=n;i++)
{
scanf("%s",op);
int x,y;
scanf("%d%d",&x,&y);
mp[op[0]]=min(x,y);
}
for(int i=1;i<=l;i++)
f[i][i-1]=f[i][i]=0;//无效字符串和单个字符的代价都是0
for(int len=2;len<=l;len++)
for(int i=1;i+len-1<=l;i++)
{
int j=i+len-1;
if(s[i]==s[j]) f[i][j]=f[i+1][j-1];
f[i][j]=min(f[i][j],f[i+1][j]+mp[s[i]]);
f[i][j]=min(f[i][j],f[i][j-1]+mp[s[j]]);
}
cout<<f[1][l];
return 0;
}