HDU 2476 String painter 动态规划-区间dp

HDU 2476 String painter

题意:

给定一个字符串,和一个目标串,然后有一支画笔,每次可以将某一个区间内全部字符刷成你想要的字符,但是是一样的字符,比如 zzzzzfzzzzz,我可以用画笔把某一连续段刷成任意字符,我可以刷成zzaaafzzzzz,我刷了三个a。

问:最少刷几次,可以把给定字符串刷成目标串?

例如zzzzzfzzzzz,长度为11,下标看做0~10

先将0~10用画笔刷一次,变成aaaaaaaaaaa

1~9刷一次,abbbbbbbbba

2~8:      abcccccccba

3~7:       abcdddddcba

4~6:        abcdeeedcab

5:           abcdefedcab

这样就6次,变成了s2串了

每次刷一个区间


思路:

动态规划区间dp,第一次碰这种题很难理解。主要过程就是把大问题化成一个个       的小问题,从小问题逐渐把大问题递推出来

我们先从区间长度为1开始,统计每个长度为1的区间需要刷的次数,然后就能利用这些数据,去递推出长度为2的各个区间的次数。以此类推。

具体实现看代码:

#include<stdio.h>
#include<string.h>
int min(int a,int b){ return a<b?a:b; }
int dp[110][110];//dp[i][j]就代表区间[i,j],包含i,j 
int dp1[110];
char s[110],c[110];//给定字符串,目标串 
int main()
{
	while(~scanf("%s%s",s,c))
	{
		int len=strlen(s);
		memset(dp,0,sizeof(dp));
		for(int l=1;l<=len;l++) //区间长度,从小到大递推 
			for(int i=0;i<len-l+1;i++) //假设一个左端点,则右端点j=i+l-1,且<len; 
			{
				int j=i+l-1;//右端点
				    //现在记大区间为dp[i][j](未知),小区间dp[i+1][j](已求出) 
				dp[i][j]=dp[i+1][j]+1;//刷完小区间,加一次把i这个字符刷掉
				for(int k=i+1;k<=j;k++) //扫描小区间
				{
					if(c[i]==c[k])//小区间内某字符等于大区间左端点
					{ //再把小区间分开,i这个字符就可以跟随dp[i+1][k]一起刷掉 
						//再加上dp[k+1][j]即可
						//与原数值比较大小 
						dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);
					}
				} 
			}
	//到了这里还没有结束!!
	/*因为上面的dp是按左端点处理的,
	当区间长度大于1时,左端点就永远到不了字符串的末尾 
	末尾的dp也就没有全面进行 	*/

	//下面单独对右端点进行动态规划 
	for(int i=0;i<len;i++)
		dp1[i]=dp[0][i];//用dp1[i]表示区间[0][i]的最优解
	for(int i=0;i<len;i++)
	{
		if(s[i]==c[i])//该字符无需更改 
		{			//那么只需要改该字符左边的字符 
			if(i==0)//特殊情况 
				dp1[i]=0;
			else 
				dp1[i]=dp1[i-1];//因为第i个字符不需要做什么 
		}
		else
			for(int k=0;k<i;k++) //一定要从0开始, 
				dp1[i]=min(dp1[i],dp1[k]+dp[k+1][i]);
	} 
	printf("%d\n",dp1[len-1]);
	}
	return 0;
}


模板:

        memset(dp,0,sizeof(dp)); //二维 
        int i,j,l,k;  
        for(l = 2; l <= n; ++l) //假设区间长度,递推 
        {  
            for(i = 1; i <= n - l + 1; ++i)  //左端点 
            {  
                j = i + l - 1; //右端点 
                dp[i][j] = 2100000000; //初值(有时初值跟之前的状态有关系,不一定常数) 
                for(k = i; k < j; ++k)  //找中转站 
                {  
                    dp[i][j]=min(dp[i][j],dp[i][k] + dp[k + 1][j] +权值);  
                }  
            }  
        }  
        printf("%d\n", dp[1][n]); 


有时候,可能区间左端点访问不到末尾,从而得不到最优解,需要再动态规划一次,比如上面的例题。








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪的期许

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值