洛谷2187 小Z的笔记(DP)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83146309

题目

由于没有好好听课,小Z的笔记全都记的杂乱无章,出现了好多错误的地方。小Z的笔记是如此的糟糕,以至于他只记了一句例句,而且自己还不知道是什么意思……然后在老师讲语法的时候,小Z又零星的记了几个字母对,老师说,这几个字母对是绝对不能相邻的,而且相邻是不关心字母的顺序的,比如老师说,“ab”不能相邻,那么相同的,“ba”也不能相邻。
现在小Z到家了,打开了上课的笔记,然后他发现笔记有很多自相矛盾的地方:为什么下面的不能相邻的字母对会出现在上面的例句里面呢?纠结再三,小Z觉得下面的东西相对比较简单,所以记错的概率比较小……他决定在上面的例句里面擦掉几个字母,使得句子变得合法。
但是小Z还有其他作业要做呢,来不及整理笔记了,就把这个艰巨的任务留给了大家,请问大家,小Z最少要擦掉几个字母,才能使得上面的例句合法?

题解

DP
设f[i]表示前i个最少擦除个数,则有

f[i]=\min_{j<i,v[s[i],s[j]]=true}\left\{f[j]+i-j\right\},其中v[i][j]表示i和j可以放在一起。

这样做时间复杂度为O(N^2)。
我们发现其实j从1枚举到i-1,枚举了这么多,其实关注的就是26个字母最后出现的那个f[j]。
设g[c]表示以字母c结尾的最优结果。
回到DP方程上看,我们分离开来看f[i]=\min\left\{(f[j]-j)+i\right\},我们令g[c]以字母c结尾的最小f[j]-j。
所以f的转移方程就是f[i]=\min\left\{g[c]+i\}

总结

一道很好的根据公式进行优化的题,同时还精简了转移的决策,做到加速。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,m;
char s[maxn];
bool v[30][30];
int f[maxn],g[maxn];

int main()
{
    scanf("%d",&n);
    scanf("%s",s+1);
    scanf("%d",&m);
    memset(v,true,sizeof(v));
    for(int i=1;i<=m;i++)
    {
        char c[3];
        scanf("%s",c);
        c[0]-='a';c[1]-='a';
        v[c[0]][c[1]]=v[c[1]][c[0]]=false;
    }
    for(int i=1;i<=n;i++)
    {
        int c=s[i]-'a';
        f[i]=i;
        for(int j=0;j<26;j++) if(v[c][j])
        {
            f[i]=min(f[i],g[j]+i-1);
        }
        g[c]=min(g[c],f[i]-i);
    }
    printf("%d\n",f[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83146309