UVA1437 String painter(区间dp)

原题链接

题意

有两个用小写英文字母组成的等长字符串。你有一个强大的字符串刷子,在这把刷子的帮助下,你可以将一个字符串的一个字串中的字符全部刷成任何你想要的字符。也就是说,用刷子刷过的字串就变成用同一个字母组成的了。现在你想要用这一把刷子把字符串A刷成B,而且要求刷的次数最少。

思路

本题的题意与涂色类似。不同之处在于本题有些位置上的字符本身就相等,没必要修改。

可以发现如果修改的两个区间是相交的关系,那么这个交区间就等价于只修改了后一次,那么前一次修改的区间就没必要再包含这个交区间了。于是就可以知道,一定存在一组最优解,使得所有修改的区间的关系只有包含和无交集。那么就可以用区间 dp解决本题。

\(f[i][j]\) 表示将 A 转化为 B 时不考虑本身相等的字符所需的最少修改次数。得到其中一个状态转移方程:

\(f[i][j]=\min(f[i][k]+f[k+1][j])\)

此外,如果两个端点的字符相等,那么就可以将扩大修改的边界,即:

\(f[i][j]=\min(f[i+1][j],f[i][j-1])\)

接下来考虑不需要修改的情况。不难发现,把相等的字符去掉后,就剩下了若干个子区间。那么就可以从前往后考虑是否需要断开区间。用 \(g[i]\) 表示将 \([1,i]\) 的字符串修改成一致所需的最少次数。得到状态转移方程:

\(g[i]=\min(g[j],f[j+1][i])\)

如果此时不需要修改,即 \(a[i]==b[i]\) 那么就是:

\(g[i]=g[i-1]\)

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,INF=0x3f3f3f3f;
int n,f[N][N],g[N];char a[N],b[N];
int main()
{
	while(scanf("%s%s",a+1,b+1)==2)
	{
		memset(f,0x3f,sizeof(f));n=strlen(a+1);
		for(int i=1;i<=n;i++) f[i][i]=1;
		for(int len=2;len<=n;len++)
		    for(int l=1,r=l+len-1;r<=n;l++,r++)
		    {
		    	if(b[l]==b[r]) f[l][r]=min(f[l][r-1],f[l+1][r]);
		    	for(int k=l;k<r;k++) f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
			} 
		for(int i=1;i<=n;i++)
		{
			g[i]=f[1][i];
			if(a[i]==b[i]) g[i]=min(g[i],g[i-1]);
			for(int j=1;j<i;j++) g[i]=min(g[i],g[j]+f[j+1][i]);
		}
		printf("%d\n",g[n]);
	}
	return 0;
}
posted @ 2022-06-28 21:02  曙诚  阅读(42)  评论(0)    收藏  举报