P4170 木板涂色(区间DP)

题目描述:

题目传送门


解题思路:

此题可以考虑使用区间 d p dp dp 求解。

a i a_i ai 表示为目标状态中第 i i i 个位置的颜色。
f i , j f_{i,j} fi,j 表示在一个 ( i , j ) (i,j) (i,j) 的区间内,使这个区间达到目标颜色需要的最少涂色次数。
可以考虑分类讨论两种情况:

  1. a i = a j a_i=a_j ai=aj也就是说为 i i i 上色时可以同时为 j j j 上色,因此 j j j 可以被省略,也就是说这个状态等同于 f i , j − 1 f_{i,j-1} fi,j1;也可以看做为 j j j 上色时顺便将 i i i 上色,因此 i i i 也能被省略,即为 f i + 1 , j f_{i+1,j} fi+1,j

  2. a i ≠ a j a_i\ne a_j ai=aj也就是说 i i i j j j 不能同时上色,也就是说 i i i j j j 其实可以看做是两个子区间的部分,而区间 ( i , j ) (i,j) (i,j) 则是由这连个子区间加起来得来的。因此我们用到区间 d p dp dp 的基本思想,枚举这两个子区间的断点(分割点) k k k ,使得 f i , k − 1 + f k , j f_{i,k-1}+f_{k,j} fi,k1+fk,j 最小。

推出状态转移方程:
f i , j = { min ⁡ ( f i + 1 , j , f i , j − 1 ) a i = a j min ⁡ ( f i , k − 1 + f k , j ) a i ≠ a j , k = i + 1 ⋯ j f_{i,j}=\begin{cases} \min (f_{i+1,j},f_{i,j-1})&a_i=a_j\\ \\ \min(f_{i,k-1}+f_{k,j})&a_i\ne a_j,k=i+1\cdots j \end{cases} fi,j=min(fi+1,j,fi,j1)min(fi,k1+fk,j)ai=ajai=aj,k=i+1j

考虑初始状态:若要涂的区间长度为 1 1 1 ,很显然涂的次数最少即为 1 1 1
f i , j = { 1 i = j ∞ i ≠ j f_{i,j}=\begin{cases} 1&i=j\\ \\ \infty&i\ne j \end{cases} fi,j=1i=ji=j


CODE:

#include <iostream>
#include <cstring>
using namespace std;
string a;
int f[60][60];
int main()
{
	cin>>a;
	a=" "+a;
	for(int i=0;i<=a.size();i++)
	  for(int j=0;j<=a.size();j++)
	    f[i][j]=0x3f3f3f3f;
	f[0][0]=0;
	for(int i=1;i<=a.size();i++)
	  f[i][i]=1;
	int n=a.size();
	for(int len=2;len<=n;len++)
	  {
	  	for(int l=1;l+len-1<=n;l++)
	  	  {
	  	  	int r=l+len-1;
	  	  	if(a[l]==a[r])
	  	  	  {
	  	  	  	f[l][r]=min(f[l+1][r],f[l][r-1]);
	  	  	  	continue;
			  }
			for(int k=l+1;k<=r;k++)
			  {
			  	f[l][r]=min(f[l][k-1]+f[k][r],f[l][r]);
			  }
		  }
	  }
	cout<<f[1][n-1];  //减1是因为n为a的长度,但并非最右端点。
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值