HDU2476 String painter (区间dp+思维详细题解)

对于这道题,我发现好多网上的题解对dp的物理意义讲的并不是十分透彻,但是代码又是正确的,这样初学者会感到十分难以接收dp的正确性

在这篇题解,我会讲解我的dp方程的物理意义(其实是对网上代码的理解之后的自己的想法),这题我觉得思维难度还是挺大的,如果是第一次做的话

首先我们看到什么改变字符串,又是最小,很容易想到dp,并且是区间dp

所以我们自然而然地会想到f[l][r],表示将a转移成b的最小代价,其实这样也能做,但是我们下次遇到这种题目的话,可以先转换成空串转移到b串,之后再与a串进行对比

下面我们来讲解代码中的难点

1.首先我们要相信这个状态定义的正确性,如果我们在l-r范围是最小的,那么显然在大区间中使用这个答案是没有问题的,不可能说我整个大区间的最小值不是由小区间转移过来的

因为区间dp是按照长度来一层层递增的,我们在更新每个大1区间的时候,都是根据小区间的最小值转移过来,这样这个大区间就是最小的,同理可以一直递推上去。

2.为了更好理解,我们可以先对长度为1的区间进行初始化,所有f[i][i]=1,这是很合理的。

3.核心代码讲解

for(len=2;len<=n;len++){
            for(l=1;l+len-1<=n;l++){
                r=l+len-1;
                f[l][r]=f[l][r-1]+1;
                for(k=l;k<r;k++){
                    if(b[k]==b[r]){
                        f[l][r]=min(f[l][r],f[l][k-1]+f[k][r-1]);
                    }
                }
            }
        }

首先f[l[][r]=f[l][r-1]+1,因为我们最坏的情况就是直接对这个点进行改变成和b相等的情况

之后我们枚举每个断点,这也是区间dp的常见手法,这里的意义就是如果我们在这段区间内有一个点b[k]的值和b[r]的值是相同的,那么就有可能可以优化

因为k和r相等,那么我们在涂k的时候,完全可以先把k-j都涂成相同的值,这样我们涂k-r就相当于涂k--r-1,因为我们肯定要涂k,在这个时候顺便把k-r涂成同一个值,所以涂r就可以不用额外算

这样改变的最小值就成为f[l][k-1](前k-1的最小值)+f[k][r-1];

4.之后我们再考虑a串的情况

最坏的情况就是跟空串一样,所以先赋初值。

显然如果a[i]==b[i],ans[i]=ans[i-1],ans[i]代表的是从1-i的最小代价

这样的话就可以直接continue了,不用进行下面的循环,我在网上看到他们没有continue,让我一度无法理解下面的ans的转移方程的正确性,我当时一直在想为什么相等了还会有更小的情况

实际如果相等,就不用进行下面的for循环了。

那么下面的for循环的物理意义就是,也相当于如果两个位置不相等,我们不知道从之前哪个位置涂到这个位置才会使前i个最小,所以进行暴力枚举。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int inf=0x3f3f3f3f;
int    f[102][102];
int ans[N];
int main(){
    int i;
    string s,b;
    while(cin>>s>>b){
        memset(f,0x3f,sizeof f);
        memset(ans,0,sizeof ans);
        s=" "+s;
        b=" "+b;
        int j;
        int n=s.size();
        for(i=1;i<=n;i++){
            f[i][i-1]=0;
        }
        
        for(i=1;i<=n;i++){
            f[i][i]=1;
        }
        int len,l,r;
        int k;
        for(len=2;len<=n;len++){
            for(l=1;l+len-1<=n;l++){
                r=l+len-1;
                f[l][r]=f[l][r-1]+1;
                for(k=l;k<r;k++){
                    if(b[k]==b[r]){
                        f[l][r]=min(f[l][r],f[l][k-1]+f[k][r-1]);
                    }
                }
            }
        }
        for(i=1;i<=n;i++){
            ans[i]=f[1][i];
            if(s[i]==b[i]){
                ans[i]=ans[i-1];
                continue;
            }
            for(int j=1;j<=i;j++)
            ans[i]=min(ans[i],ans[j-1]+f[j][i]);
        }
        cout<<ans[n]<<endl;
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/ctyakwf/p/12328992.html