Codeforces Round #538 (Div. 2) D - Flood Fill(区间dp)

题目

给你一个序列c[],每个位置有一个数值代表颜色

初始选定某一个起始位置,

可以将包含起始位置连续相同颜色段换成任意颜色,

换一次cost+1,

问最后所有颜色相同时,cost最小为多少

思路来源

https://blog.csdn.net/toohandsomeIeaseId/article/details/86983883

题解

很经典的区间dp问题

大概就是删啊变啊最小cost那种

然而区间dp做的还是不够多

本题注意到合并之后[l,r]的颜色只能与a[l]或a[r]相同

这就可以dp了,只有两种转移状态

dp[l][r][0]代表当前颜色为a[l]的最小cost

dp[l][r][1]代表当前颜色为a[r]的最小cost

[l,r-1]与a[r]合并的时候,只能变成a[r]的颜色,去看[l,r-1]的颜色是a[l]还是a[r-1]即可

同理[l+1,r]与a[l]合并的时候,只能变成a[l]的颜色,去看[l+1,r]的颜色是a[l+1]还是a[r]即可

	dp[l][r][1]=min(dp[l][r-1][0]+(a[l]!=a[r]),dp[l][r-1][1]+(a[r-1]!=a[r])) 变成r的颜色
	dp[l][r][0]=min(dp[l+1][r][0]+(a[l]!=a[l+1]),dp[l+1][r][1]+(a[l]!=a[r])) 变成l的颜色 

代码

看个人喜好,有三种不同的写法,

前两种是区间长度的扩张,[l,r]总是从[l,r-1]和[l+1,r]的最优中得来

而最后一种是当前状态向外两种状态的拓展,[l,r]可以向[l-1,r]和[l,r+1]转移

个人喜欢第一种,枚举长度len,枚举左端点l,比较套路

第二种则是固定右端点,每次从右往左扫,[l,r]总是从[l,r-1]和[l+1,r]得来,

[l,r-1]在上一轮r已经得出,而[l+1][r]在本轮r的上一内循环l也已得出,故可行

#include<iostream>
#include<cstdio>
#include<cmath> 
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=5e3+10;
const int INF=0x3f3f3f3f; 
//dp[l][r][0]:[l,r]最小合并次数,且最终颜色为a[l]
//dp[l][r][1]:[l,r]最小合并次数,且最终颜色为a[r]
//得开一维记录当前区间的颜色 显然与端点相同 这点很重要 
int dp[maxn][maxn][2],a[maxn];
int n;
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;++i)
	scanf("%d",&a[i]);
	for(int i=0;i<n;++i)
	{
		for(int j=0;j<n;++j)
		{
			for(int k=0;k<2;++k)
			{
				if(i!=j)dp[i][j][k]=INF;
			    else dp[i][j][k]=0;
			}
		}
	} 
	//对于某个给定区间[l,r],尝试左扩一个,再尝试右扩一个 
	//dp[l][r][1]=min(dp[l][r-1][0]+(a[l]!=a[r]),dp[l][r-1][1]+(a[r-1]!=a[r])) 变成r的颜色
	//dp[l][r][0]=min(dp[l+1][r][0]+(a[l]!=a[l+1]),dp[l+1][r][1]+(a[l]!=a[r])) 变成l的颜色 
	for(int len=2;len<=n;++len)
	{
		for(int l=0;l+len-1<n;++l)
		{
			int r=l+len-1;
			dp[l][r][1]=min(dp[l][r-1][0]+(a[l]!=a[r]),dp[l][r-1][1]+(a[r-1]!=a[r])); 
			dp[l][r][0]=min(dp[l+1][r][0]+(a[l]!=a[l+1]),dp[l+1][r][1]+(a[l]!=a[r]));
		}
	}
	printf("%d\n",min(dp[0][n-1][0],dp[0][n-1][1]));
	return 0;
}

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Code92007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值