[SDOI2006]最短距离


洛谷题目链接

声明:

本篇文章只大概讲思路


 原串设为$s1$,目标串设为$s2$,$n1,n2$分别为他们的长度

我们考虑$dp$,设$f[i][j]$表示$s1$中删除到了第$i$个字符,$s2$中添加到了第$j$个字符,那么对于每种操作,我们如下转移:

$1、Delete$:(删除操作,不需要判断,直接转移,此时的费用为$s1$删除到$i-1$,$s2$添加到$j$时的费用加上删除的费用并且取最小值)

$f[i][j]=min(f[i][j],f[i-1][j]+pay[1]$

$2、Repalce$:(替换操作,不需要判断,直接转移,此时的费用为$s1$删除到$i-1$,$s2$添加到$j-1$时的费用加上费用取最小值)

$f[i][j]=min(f[i][j],f[i-1][j-1]+pay[2])$

$3、Copy$:(复制操作,需要判断,当现在的$i,j$相同时转移)

$if(s1[i]==s2[j])
f[i][j]=min(f[i][j],f[i-1][j-1]+pay[3]);$

$4、Insert$:(插入操作,不需要判断,直接转移)

$f[i][j]=min(f[i][j],f[i][j-1]+pay[4])$

$5、Twiddle$:(需要判断,当$i>=2&&j>=2&&s1[i-1]==s2[j]&&s1[i]==s2[j-1]$的时候转移)

$if(i>=2&&j>=2&&s1[i-1]==s2[j]&&s1[i]==s2[j-1])$

$f[i][j]=min(f[i][j],f[i-2][j-2]+pay[5])$

$6、Kill$:(单独拿出来用,最后判断一下)

$for(int i=1;i<n1;++i)$
$f[n1][n2]=min(f[n1][n2],f[i][n2]+(n1-i)*pay[1]-1)$

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 210
#define inf 0x3f3f3f3f
using namespace std;
char s1[N],s2[N];
int n1,n2; 
int pay[6],f[N][N];//f[i][j]中i表示s1中已经删除到i个字符,j表示s2串中已经添加到j个字符 
int main()
{
	scanf(" %s %s",s1+1,s2+1);
	n1=strlen(s1+1),n2=strlen(s2+1); 
	for(int i=1;i<=5;++i)
		scanf("%d",&pay[i]);
	for(int i=1;i<=n1;++i)
		for(int j=1;j<=n2;++j)
			f[i][j]=inf;
	for(int i=1;i<=n1;++i)
		f[i][0]=pay[1]*i;
	for(int i=1;i<=n2;++i)
		f[0][i]=pay[4]*i;
	f[0][0]=0;
	for(int i=1;i<=n1;++i)
	{
		for(int j=1;j<=n2;++j)
		{
			if(s1[i]==s2[j])
				f[i][j]=min(f[i][j],f[i-1][j-1]+pay[3]);
			f[i][j]=min(f[i][j],f[i-1][j]+pay[1]);
			f[i][j]=min(f[i][j],f[i][j-1]+pay[4]);
			f[i][j]=min(f[i][j],f[i-1][j-1]+pay[2]);
			if(i>=2&&j>=2&&s1[i-1]==s2[j]&&s1[i]==s2[j-1])
				f[i][j]=min(f[i][j],f[i-2][j-2]+pay[5]);
		}
	 } 
	for(int i=1;i<n1;++i)
		f[n1][n2]=min(f[n1][n2],f[i][n2]+(n1-i)*pay[1]-1);
	printf("%d",f[n1][n2]);
}

  

原文地址:https://www.cnblogs.com/yexinqwq/p/10178103.html