题解:[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]\),有转移:

\[f_{i,j} = \min_{k = i}^{j - 1}f_{i,k} + f_{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]\) 变成无色。有转移:

\[g_{i,j} = \min_{k = i}^{j - 1}g_{i,k} + f_{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;
}
posted @ 2025-04-06 21:17  伊埃斯  阅读(56)  评论(0)    收藏  举报