[ABC400F] Happy Birthday! 3

题目大意

在一个 \(n\) 等份的圆上,每次可以给连续区间染色(扇形),代价是该种颜色代价加长度。问:最初无色,染色到目标状态的最小花费。

题解

其实,很明显可以看出是一道动态规划的题目。发现数据看上去可以 \(O(n^3)\) 过,考虑区间 DP。

发现直接从无色开始染很难做,考虑倒过来,从目标状态做到无色。

\(f_{i,j}\) 表示把 \(i\)\(j\) 染成无色的最小代价,很明显有 \(f_{i,j}=\min^{j-1}_{k=i}f_{i,k}+f_{k+1,j}\)。但是因为一段区间里的颜色可能不一样,所以要让它可以被染成无色。我们再设 \(g_{i,j}\) 表示把 \(i\)\(j\) 整成可以拿去染成无色(即这一段颜色相同)的最小代价,很明显有 \(g_{i,j}=\min^{j-1}_{k=i}g_{i,k}+f_{k+1,j}\),解释一下后面为什么要加 \(f_{k+1,j}\):因为此时已经保证(长度小,前面算过)\(i\)\(k\) 区间颜色相同,但是剩下的区间的颜色不一定与该区间颜色相同,所以直接用无色的代价。

特别的,当 \(c_i=c_j\) 时,可以不操作,让 \(g_{i,j}=\min(g_{i,j},g_{i,j-1})\),同时可以通过操作整成无色 \(f_{i,j}=\min(f_{i,j},g_{i,j}+len+X_{c_i})\),其中 \(len\) 是区间的长度,\(X_{c_i}\) 是该颜色的特有代价。

记得破环成链!

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e3+7;
const int inf=1e18;
int f[MAXN][MAXN],g[MAXN][MAXN];
int n,c[MAXN],val[MAXN];
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
		c[i+n]=c[i];
	}
	for(int i=1;i<=n;i++)
		scanf("%lld",&val[i]);
	for(int i=1;i<=2*n;i++)
		for(int j=1;j<=2*n;j++)
			f[i][j]=g[i][j]=inf;
	for(int i=1;i<=n*2;i++)
	{
		f[i][i]=1+val[c[i]];
		g[i][i]=0;
	}
	for(int len=2;len<=n;len++)
	{
		for(int l=1;l+len-1<=2*n;l++)
		{
			int r=l+len-1;
			for(int k=l;k<r;k++)
			{
				f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
				g[l][r]=min(g[l][r],g[l][k]+f[k+1][r]);
			}
			if(c[l]==c[r])
			{
				g[l][r]=min(g[l][r],g[l][r-1]);
				f[l][r]=min(f[l][r],g[l][r]+len+val[c[l]]);
			}
		}
	}
	int ans=inf;
	for(int i=1;i<=n;i++)
		ans=min(ans,f[i][i+n-1]);
	printf("%lld",ans);
	return 0;
}

谢谢观看!
记得开 long long

posted @ 2025-04-07 13:27  HHMing  阅读(28)  评论(0)    收藏  举报