Codeforces Round #336 (Div. 1) 607B Zuma 回文dp小总结

题意

  • 给你一个数列,你每秒可以从中删去一个回文子串,问你最少多少秒可以删完这个子串

思路

  • 其实这类回文dp,方法都是类似的,用dp[i][j]表示从i到j这个子串最少多少秒可以被删完
  • 然后我们从中间向两边看更新dp,用dp[i][k]和dp[k+1][j]更新一下dp,再考虑a[i] == a[j]时单独更新一下dp就行了。
  • 比如之前做过的最长回文子序列,也是类似的想法
  • 具体来说这题,转移,对于一般情况,dp[i][j] = min(dp[i][k] + dp[k+1][j], dp[i][j]),这个不难想到,即先删去左半边,再删去右半边,看怎么删最好。当a[i] == a[j]时,就麻烦一些,我一开始在这也有点犯晕。。其实,我们这么想,这次操作,一定是把中间的某些子串先删去,最后剩下包含a[i]和a[j]的串正好是回文的,然后我们一下就删掉了。那么,dp[i+1][j-1]正好就可以帮助我们来解决问题,因为它也是类似的方法删去它中间的一些子串,剩下一个回文串,再把它删掉,所以我们可以在还剩下一个回文串时,把a[i]和a[j]加到这个串上,它还是回文的,所以我们就得到了转移,dp[i][j] = min(dp[i][j] , dp[i+1][j-1])。
  • 这题,我稍微预处理了一下,把找出了其中全部的回文串,让dp = 1。

实现

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 505;
int dp[maxn][maxn];
int a[maxn];

int main(){
    int n;
    memset(dp,0x3f,sizeof(dp));
    cin>>n;
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        dp[i][i] = 1;
    }
    for (int len=1;len<n;len++){
        for (int i=1;i+len<=n;i++){
            int j = i + len;
            if (a[i] != a[j]){
                continue;
            }
            if (len == 1 || dp[i+1][j-1] == 1){
                dp[i][j] = 1;
            }
        }
    }

    for (int len=1;len<n;len++){
        for (int i=1;i+len<=n;i++){
            int j = i + len;
            for (int k=1;k<=len;k++){
                dp[i][j] = min(dp[i][j],dp[i+k][j] + dp[i][i+k-1]);
            }
            if (a[i] == a[j] && dp[i][j] > 1){
                dp[i][j] = min(dp[i][j],dp[i+1][j-1]);
            }
        }
    }

    cout << dp[1][n] << "\n";
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值