题解:[ABC400F] Happy Birthday! 3
思路
做法来自官方题解。
我们发现正着做很难做,考虑正难则反。将题目改为:有一个长度为 \(n\) 的环,环上每个点有一个颜色。每次操作可以花费 \(len + v_{c_i}\) 的操作将 \([i,i + len - 1]\) 的每个点漂白,当且仅当这一段区间内的点要么没有颜色,要么颜色为 \(c_i\),求将整个环漂白的最小代价。
我们发现这玩意很区间 dp 啊,直接设 \(f_{i,j}\) 表示将 \([i,j]\) 漂白的最小代价。我们考虑最后一次操作,如果操作的区间 \([i',j']\) 不是 \([i,j]\) 但是与 \([i,j]\) 有交,那么考虑两种交的情况:
- \(i' \in [i,j]\),可以将这个区间分成 \(f_{i,i'} + f_{i' + 1,j}\)。
- \(j' \in [i,j]\),可以将这个区间分成 \(f_{i,j'} + f_{j' + 1,j}\)。
总之我们可以将 \([i,j]\) 分成 \([i,k] \cup [k + 1,j]\),有转移:
那么对于最后一次操作在 \([i,j]\) 上的,那么一定有 \(c_i = c_j\)。因为如果 \(c_j \neq c_i\) 那么 \(c_j\) 就只能是 \(0\),都是 \(0\) 了就没必要花多的代价漂白它了。转移有 \(f_{i,j} = val + len + v_{c_i}\)。其中 \(val\) 表示将 \([i,j]\) 整成能漂的最小代价。
考虑如何求 \(val\)。设 \(g_{i,j}\) 表示将 \([i,j]\) 整成能漂的最小代价。如何转移呢?在上面转移 \(f_{i,j}\) 的时候,我们想想如何转移 \(g\)。我们将它分成两个区间 \([i,k] \cup [k + 1,j]\),我们想让它能漂,那么 \([i,k]\) 是得能漂的。然后我们想当然的就想:\([k + 1,j]\) 也能漂就足够了。但是这显然是不行的。因为如果 \([k + 1,j]\) 也能漂,我们又不知道 \([k + 1,j]\) 这里面的一样的颜色是啥。所以只能让 \([k + 1,j]\) 变成无色。有转移:
我们考虑当 \(c_i = c_j\) 时如何转移。有 \(g_{i,j} \leftarrow g_{i,j - 1}\),表示第 \(j\) 个不操作。
最后的答案显然是 \(\min_{i = 1}^{n}f_{i,i + n - 1}\)。
code
#include<bits/extc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n;
int c[805],v[405];
int dp1[805][805],dp2[805][805];
// dp1 -> f,dp2 -> g
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> c[i];
c[i + n] = c[i];
}
for (int i = 1; i <= n; i++)
cin >> v[i];
memset(dp1,0x3f,sizeof dp1);
memset(dp2,0x3f,sizeof dp2);
for (int i = 1; i <= n << 1; i++)
{
dp1[i][i] = 1 + v[c[i]];
dp2[i][i] = 0;
}
for (int len = 2; len <= n; len++)
{
for (int i = 1,j; (j = i + len - 1) <= n << 1; i++)
{
for (int k = i; k < j; k++)
{
dp1[i][j] = min(dp1[i][j],dp1[i][k] + dp1[k + 1][j]);
dp2[i][j] = min(dp2[i][j],dp2[i][k] + dp1[k + 1][j]);
}// 第一个转移
if (c[j] == c[i])
{
dp2[i][j] = min(dp2[i][j],dp2[i][j - 1]);
dp1[i][j] = min(dp1[i][j],dp2[i][j] + len + v[c[j]]);
}// 第二个转移
}
}
int ans = inf;
for (int i = 1; i <= n; i++)// 最后的答案
ans = min(ans,dp1[i][i + n - 1]);
cout << ans;
return 0;
}
本文来自博客园,使用 CC BY-NC-SA 4.0 协议。
作者:伊埃斯,转载请注明原文链接:https://www.cnblogs.com/Eous/articles/18811677