P4170 木板涂色(区间DP)

Toretto·2021-12-07 20:01·21 次阅读

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;
} 
posted @   S·A·I  阅读(21)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示