我看到一篇题解是这样定义状态的: 令f[i][j][k][first]为在第i个位置,本位置选了第j种树,前一个位置选第k种树,第一棵树种的是first时的最大观赏值
那么在第n棵树的时候, 状态dp[n][j][k][first], 只能知道第n - 1棵树, 第n棵树, 第一棵树分别种了第k, j, first类别的树,此时可以判断第n棵树的状态是否符合都比相邻的高或低的要求. 但是却不能判断第一棵树的状态是否也符合要求, 因为第一棵树的状态判断需要知道第二棵树种了什么.
所以不知道是数据过弱,还是我想的有问题?
下面是我考虑第一棵树以及第二棵树分别种了什么类别的树的AC代码.
ps. 记录首的状态是什么, 通常是解决环的一个好办法, 因为首尾相连, 当我们考虑到尾的时候, 如果还知道首的状态, 会简单处理很多.
#include <bits/stdc++.h>
#define ll long long
using namespace std;
static const int N = 1e5 + 5;
const int INF = 1e9;
// dp[i][j][k][first][second]表示考虑前i棵树, 第i棵树种第j类树, 第i - 1棵树种第k类树
// 且第一棵树和第二棵树分别种第first, second类树, 能获得的最大值.
// 时间复杂度: 状态数 * 转移数 = O(n * 3 * 3 * 3 * 3) * O(3) = O(n * (3 ^ 5))
int n, a[N][4], dp[N][4][4][4][4];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i][1] >> a[i][2] >> a[i][3];
}
int ans = 0;
for (int first = 1; first <= 3; ++first) {
for (int second = 1; second <= 3; ++second) {
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= 3; ++j) {
for (int k = 1; k <= 3; ++k) {
// dp[i][j][k][first][second];
int &ret = dp[i][j][k][first][second];
if (i == 2) {
if (j == second && k == first) ret = a[i][j] + a[i - 1][k];
else ret = -INF;
} else {
// 第i棵树种第j类树, 第i - 1棵树种第k类树, 枚举第i - 2棵树种那类树.
// u, k, j
for (int u = 1; u <= 3; ++u) {
if (u < k && k > j) {
ret = std::max(ret, dp[i - 1][k][u][first][second] + a[i][j]);
}
if (u > k && k < j) {
ret = std::max(ret, dp[i - 1][k][u][first][second] + a[i][j]);
}
}
}
// 检查第n棵树以及第1棵树是否符合要求.
if (i == n) {
if ((first < j && j > k) && (first < j && first < second)) ans = std::max(ans, ret);
if ((first > j && j < k) && (first > j && first > second)) ans = std::max(ans, ret);
}
}
}
}
}
}
cout << ans;
return 0;
}