CSP-S 2024 T3 染色 题解

一道自认为特别好的 dp 题,虽然水,但是感觉很适合刚学 dp 的人做的一道题。

题目链接:https://www.luogu.com.cn/problem/P11233


考虑 dfs 暴力,枚举每一个位置的颜色,记录当前位置上一个的红色和蓝色的数的值,在中途统计答案。时间复杂度:\(O(2^n)\)


考虑将 dfs 暴力转化为 dp,将 dfs 中记录的状态转化为 \(f_{i,j,k}\),表示选到第 \(i\) 个,从第 \(i\) 个数起,从右往左第一个红色数为 \(j\),第一个蓝色数为 \(k\) 的最大代价,转移和 dfs 基本一样。时间复杂度:\(O(n^3)\)


容易发现一个性质,我们没必要将 \(j\)\(k\) 都记录下来,因为 \(j\)\(k\) 中一定有一个为 \(a_i\),这样又可以把状态设计为 \(f_{i,j,0/1}\) 表示选到第 \(i\) 个数,第 \(i\) 个数的颜色为 \(0/1\)(红或蓝),上一个与第 \(i\) 个数异色的数为 \(j\) 的最大代价。转移方程为:

\[f_{i,j,o} \gets f_{i-1,j,o}+a_i[a_i=a_{i-1}] \]

\[f_{i,a_{i-1},o} \gets \max_j \{f_{i-1,j,\neg o} + j[j=a_i]\} \]

时间复杂度:\(O(n^2)\)


将第二个转移式拆分为两个:

\[f_{i,a_{i-1},o}\gets \max_j f_{i-1,j,\neg o} \]

\[f_{i,a_{i-1},o}\gets f_{i-1,a_i,1}+a_i \]

对于这三个转移式,我们首先可以将 \(i\) 通过滚动数组滚掉,然后可以维护一个全局 \(\max\) 和一个全局加的 tag,这样就可以做到 \(O(n)\) 的转移了。

时间复杂度:\(O(n)\)


代码如下:

#include <bits/stdc++.h>

using namespace std;

const int kMaxN = 1e6 + 5;

int T, n, maxa, a[kMaxN];
long long f[kMaxN][2], ans, g[2], h[2];

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  for (cin >> T; T; T--, maxa = ans = 0) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
      cin >> a[i], maxa = max(maxa, a[i]);
    }
    for (int i = 0; i <= maxa; i++) {
      f[i][0] = f[i][1] = -1e18;
    }
    f[0][0] = f[0][1] = g[0] = g[1] = h[0] = h[1] = 0;
    for (int i = 1; i <= n; i++) {
      long long tmp[2] = {};
      for (int o = 0; o < 2; o++) {
        tmp[o] = max({f[a[i - 1]][o], g[o ^ 1] - a[i] * (a[i] == a[i - 1]), f[a[i]][o ^ 1] + a[i] - a[i] * (a[i] == a[i - 1])});
      }
      for (int o = 0; o < 2; o++) {
        h[o] += a[i] * (a[i] == a[i - 1]), g[o] = max(g[o], f[a[i - 1]][o] = tmp[o]);
      }
    }
    for (int i = 0; i <= maxa; i++) {
      ans = max(ans, max(f[i][0] + h[0], f[i][1] + h[1]));
    }
    cout << ans << '\n';
  }
  return 0;
}

水题,但是考试上没有细想,以为题目太难,直接开始打 T4 暴力,如果仔细想一下的话,肯定是能够想出来的。

posted @ 2024-10-28 19:24  liruixiong0101  阅读(45)  评论(0编辑  收藏  举报