[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!

浙公网安备 33010602011771号