CF1132F Clear the String

太久没有搞信息了果然会RP--。

考场竟然没有看出来是区间dp,胡乱打了个贪心,好在其他题问题不大。


子状态为\(dp[i][j]\)表示消除区间\([i,j]\)内所有字母所需的最小步数。

我们从两个方向考虑转移。

  1. 与上一个状态相比,新的字母不能缩短步数,那么直接加1。

  2. 可以缩短步数。那么枚举与哪一个字母相同。

第一种转移为\(dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1\)

第二种转移枚举\([i,j]\)中的\(k\),然后有\(dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j])\)

在维护上有一个需要注意的细节:因为第一种转移需要\(dp[i+1][j]\)\(dp[i][j-1]\)的数据,所以我们需要先枚举\(j\)再枚举\(i\),同时\(j\)从大到小枚举。


AC代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

namespace StandardIO {

    template<typename T>inline void read (T &x) {
        x=0;T f=1;char c=getchar();
        for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
        for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
        x*=f;
    }

    template<typename T>inline void write (T x) {
        if (x<0) putchar('-'),x*=-1;
        if (x>=10) write(x/10);
        putchar(x%10+'0');
    }

}

using namespace StandardIO;

namespace Fate {
    
    const int N=1010;
    
    int n;
    char c[N];
    int dp[N][N]; // min steps for clearing [i,j]
    
    inline void Grand_Order () {
        read(n);
        for (register int i=1; i<=n; ++i) {
            cin>>c[i];
            dp[i][i]=1;
        }
        for (register int r=2; r<=n; ++r) {
            for (register int l=r-1; l>=1; --l) {
                dp[l][r]=min(dp[l+1][r],dp[l][r-1])+1;
                for (register int k=l; k<=r; ++k) {
                    if (c[l]==c[k]) {
                        dp[l][r]=min(dp[l][r],dp[l+1][k-1]+dp[k][r]);
                    }
                }
            }
        }
        write(dp[1][n]);
    }
    
}

int main () {
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    Fei_De::Gan_Bu_Guo_Ou_De();
}

猜你喜欢

转载自www.cnblogs.com/ilverene/p/10822729.html