首页 > 题解 > Uva 4394 String painter

Uva 4394 String painter

看见某dalao在做这道题,感觉很有趣。

题意:

给你两个字符串s、t,每次操作把s的某个区间变成同一个字符。问最少操作怎么把它变成t。

题解:

我们先定义一个$cnt[i]$表示前i位需要多少次操作。

显然当$s[i]==t[i]$时,$cnt[i]=cnt[i-1]$。

不相等时,我们可以枚举端点$j< i$满足$t[j]==t[i] \& s[j]!=t[j]$则

$$cnt[i]=min(cnt[i],dp(j+1,i-1,t[i])+1+(j>0?cnt[j-1]:0))$$

dp过程是找出$l-r$先涂上某字符串再恢复的最小花费。

这个转移也是枚举端点瞎搞下就好了。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 110
#define inf 0x3f3f3f3f
using namespace std;
char s[N],t[N];
int cnt[N],f[N][N][30];
int dp(int st,int ed,char now)
{
    if(f[st][ed][now-'a']!=-1)
        return f[st][ed][now-'a'];
    if(st>ed)
        return 0;
    if(st==ed)
        return now==t[st]?0:1;
    if(now==t[ed])
        return dp(st,ed-1,now);
    int ans=inf;
    for(int i=ed;i>=st;i--)
        if(t[i]==t[ed])
            ans=min(ans,dp(st,i-1,now)+dp(i,ed,t[ed])+1);
    return f[st][ed][now-'a']=ans;
}
main()
{
    while(~scanf("%s%s",s,t))
    {
        memset(f,-1,sizeof f);
        int len=strlen(s);
        if (s[0]==t[0])
            cnt[0]=0;
        else
            cnt[0]=1;
        for (int i=1;i<len;i++)
            if (s[i]==t[i])
                cnt[i]=cnt[i-1];
            else
            {
                cnt[i]=inf;
                for (int j=i;j>=0;j--)
                    if (t[j]==t[i] && s[j]!=t[j])
                        cnt[i]=min(cnt[i],dp(j+1,i-1,t[i])+1+(j>0?cnt[j-1]:0));
            }
        printf("%d\n",cnt[len-1]);
    }
}