Codeforces 67C. Sequence of Balls DP!
给你两个字符串,a和b,通过四种操作:
在任意位置添加一个字符花费为t1
删除任意一个字符花费为t2
改变任意一个字符花费为t3
交换相邻字符花费为t4
使字符串a用最小花费变为b。
如果没有第四种操作这题就成水dp了。所以先看前三种操作。
令f[i][j]表示匹配字符串a的前i项与字符串b的前j项的最小花费,则有
f[i][j]=min( f[i-1][j]+t2, f[i][j-1]+t1 );
if (a[i]==b[j]) f[i][j]=min( f[i][j], f[i-1][j-1] );
else f[i][j]=min( f[i][j], f[i-1][j-1]+t3 );
f[i][0]=t2*i;f[0][i]=t1*i;
再考虑第四种操作。
若匹配字符串XXXbXXXa和XXaXXb,当i=8,j=6时,若想使字符串1的'a'字符与字符串2的'b'字符通过交换完成匹配,则必须从串1中找到字符'b'从串2中找到字符'a',若有一个无法找到,则该状态不能通过交换得到。考虑pi=4,pj=3之前已匹配好的情况,若想交换'a'和'b'则'a''b'必须相邻,则删除串1中'b''a'之间的所有元素,花费(i-pi-1)*t2,此时'a''b'相邻,交换ab,花费为t4,添加串2中'a''b'之间的所有元素,花费(j-pj-1)*t1,此时i,j的状态由交换完成匹配。由题意知2· te ≥ ti + td表示两次字符交换的花费大于添加与删除之和。
---------------------------------------------------------------------------------------------------------------------------
#include <iostream> #include <cstring> using namespace std; char a[4444],b[4444]; int lena,lenb; int f[4444][4444]; int ca[4444][256]; int cb[4444][256]; int t1,t2,t3,t4; int pa,pb; int main() { while (cin>>t1>>t2>>t3>>t4) { memset(f,0,sizeof(f)); memset(ca,0,sizeof(ca)); memset(cb,0,sizeof(cb)); cin>>(a+1)>>(b+1); lena=strlen(a+1); lenb=strlen(b+1); for (int i=1;i<=lena;i++) { f[i][0]=t2*i; for (int j='a';j<='z';j++) { ca[i][j]=ca[i-1][j]; } ca[i][a[i]]=i; } for (int i=1;i<=lenb;i++) { f[0][i]=t1*i; for (int j='a';j<='z';j++) { cb[i][j]=cb[i-1][j]; } cb[i][b[i]]=i; } for (int i=1;i<=lena;i++) { for (int j=1;j<=lenb;j++) { f[i][j]=min( f[i-1][j]+t2, f[i][j-1]+t1 ); if (a[i]==b[j]) f[i][j]=min( f[i][j], f[i-1][j-1] ); else f[i][j]=min( f[i][j], f[i-1][j-1]+t3 ); pa=ca[i-1][b[j]]; pb=cb[j-1][a[i]]; if (pa!=0&&pb!=0) { f[i][j]=min( f[i][j], f[pa-1][pb-1]+t4+(i-pa-1)*t2+(j-pb-1)*t1 ); } } } cout<<f[lena][lenb]<<endl; } return 0; }