HDU 2476 String painter(区间DP)

这题分为两步dp;

第一步 dp出由一个空串 刷成str2 的每个区间[i ,j ]最少需要多少次

第二步再得出答案,具体,如果str1[i] == str2[i] 则说明i这个地方不用刷,ans[i] = ans[i-1],否则在[0,i ]之间找一个位置使得,ans[j] + dp[j+1][i] 最小。ans[n]即是答案。


这题也是看题解做出来的,看的几个题解在第一步求dp[i][j]的时候枚举区间的时候都是倒过来的枚举的,理解之后发现,应该是为了保证 dp[i][j] = dp[i+1][j] + 1这一句,既然在算i之前要用到i+1,那就从倒过来n算到1。

后来我想想发现其实是不必倒过来的,因为在正常枚举区间的时候,是从区间长度从大到小枚举的,显然[i+1,j] 比 [i ,j] 长度要小1,所以在算dp[i][j] 的时候dp[i+1][j] 也已经算好了。


而为什么不能是dp[i][j] = dp[i][j-1] + 1,即从左边继承呢?

因为我们是想枚举一个k,如果 str2[k] = str2[i],则存在一种方法是把区间[i,k]全刷成一种字符再处理[i,k]里面的,所以这个k要从i+1开始枚举,并且到 j 结束,所以不能损失掉 j 这个位置的信息,所以只能从右边继承。

【代码】

/* ***********************************************
Author        :angon

************************************************ */
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC)
#define lld %I64d
#define REP(i,k,n) for(int i=k;i<n;i++)
#define REPP(i,k,n) for(int i=k;i<=n;i++)
#define scan(d) scanf("%d",&d)
#define scanl(d) scanf("%I64d",&d)
#define scann(n,m) scanf("%d%d",&n,&m)
#define scannl(n,m) scanf("%I64d%I64d",&n,&m)
#define mst(a,k)  memset(a,k,sizeof(a))
#define LL long long
#define N 105
#define mod 1000000007
inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;}

int dp[N][N],ans[N];
char s[N],t[N];
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%s%s",s,t))
    {
        int n = strlen(s);
        REP(i,0,n+1) dp[i][i] = 1;
        for(int l=1;l<n;l++) //枚举区间长度
        {
            for(int i =0; i+l<n;i++)  //枚举起点
            {
                int j = i + l;
                dp[i][j] = dp[i+1][j] + 1;
                for(int k=i+1; k<=j; k++){
                    if(t[k] == t[i])
                        dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
                        //printf("dp[%d][%d] = %d dp[%d][%d] = %d dp[%d][%d] = %d\n",i+1,k,dp[i+1][k],k+1,j,dp[k+1][j],i,j,dp[i][j]);
                }
            }
        }
        REP(i,0,n) ans[i] = dp[0][i];
        for(int i=0;i<n;i++)
        {
            if(t[i]==s[i])
            {
                if(i==0) ans[i]=0;
                else ans[i] = ans[i-1];
            }
            for(int j=0;j<i;j++)
            {
                ans[i] = min(ans[i], ans[j]+dp[j+1][i]);
            }
        }
        printf("%d\n",ans[n-1]);
    }


    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值