[dp] [dp优化] ABC176F Brave CHAIN
posted on 2024-10-06 13:00:10 | under | source
每 \(3\) 个分为一组,为 \(a_{i,0/1/2}\),设 \(f_{i,a,b}\) 为处理完前 \(i\) 组并剩下 \(x,y\) 时最大价值,转移显然,枚举 \({5\choose 2}=10\) 种情况即可。
larsr 有个厉害做法,根据 \(a,b\) 是否在之后用到进行讨论,然后进行跳跃式决策,很巧妙。我太菜了不会,所以来看看常规做法:
状态不好优化,所以从转移入手。记 \(c,d,e\) 是 \(a_{i,2},a_{i+1,0},a_{i+1,1}\),那么可以根据选择几个原来的(即 \(a,b\))分为三类:
-
不选:形如 \(f_{i,a,b}+[c=d=e]\to f_{i+1,c,d}\),取全局最值进行转移即可。
-
选一个:形如 \(f_{i,a,b+[a=c=d]}\to f_{i+1,b,e}\),假如 \(a=c=d\),\(a,e\) 固定,枚举 \(b\) 即可;否则仍然枚举 \(b\) 并取含有 \(b\) 的状态中的最值。
-
选两个:形如 \(f_{i,a,b+[c=d=e]}\to f_{i+1,a,b}\),全局加即可。
于是我们维护全局最值、钦定一维的最值和一个全局加标记即可。需要滚掉 \(i\) 这一维,不妨先将所有修改存下来,最后一起处理以避免互相影响。复杂度 \(O(n^2)\)。
注意一下,\(f_{i,a,b}=f_{i,b,a}\),但是不能只保留其中一个,也就是要同时更新它们俩,否则转移时要用 \(f_{i,a,b}\) 却只存了 \(f_{i,b,a}\) 就尴尬了,应该只有我犯了这样 SB 的错误吧。
代码
一定注意全局加是基于上一层状态的,所以修改时要减去这个全局加,否则不符合定义。
#include<bits/stdc++.h>
using namespace std;
#define pir pair<int, int>
const int N = 2e3 + 5, inf = -1e5;
int n, f[N][N], A[N][3], cg;
int hmx[N], mx, tag, ans;
struct mdy{int x, y, val;};
queue<mdy> q;
inline void solve(){
while(!q.empty()){
mdy lsy = q.front(); q.pop();
int x = lsy.x, y = lsy.y, val = lsy.val - cg;
mx = max(mx, val);
hmx[x] = max(hmx[x], val), hmx[y] = max(hmx[y], val);
f[x][y] = max(f[x][y], val), f[y][x] = max(f[y][x], val);
}
}
signed main(){
cin >> n;
for(int i = 1; i <= n; ++i) scanf("%d%d%d", &A[i][0], &A[i][1], &A[i][2]);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j) f[i][j] = hmx[i] = inf;
f[A[1][0]][A[1][1]] = f[A[1][1]][A[1][0]] = mx = hmx[A[1][0]] = hmx[A[1][1]] = 0;
for(int i = 2, c, d, e; i <= n; ++i){
c = A[i - 1][2], d = A[i][0], e = A[i][1];
//2
//cg用于修正全局加
if(cg = (c == d && d == e)) ++tag;
//1
for(int a = 1; a <= n; ++a){
if(d == e) q.push({a, c, f[a][d] + 1});
if(c == e) q.push({a, d, f[a][c] + 1});
if(c == d) q.push({a, e, f[a][c] + 1});
q.push({a, c, hmx[a]});
q.push({a, d, hmx[a]});
q.push({a, e, hmx[a]});
}
//0
q.push({c, d, max(f[e][e] + 1, mx)});
q.push({c, e, max(f[d][d] + 1, mx)});
q.push({d, e, max(f[c][c] + 1, mx)});
solve();
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j) ans = max(ans, tag + f[i][j] + (i == j && j == A[n][2]));
cout << ans;
return 0;
}
启示
- 考场由于有 \(10\) 种转移所以几乎没往优化转移上思考,所以要耐心分讨。
- 对于优化转移,善于抓住变量、不变量,可以根据变量数量进行分讨以减少工作量。

浙公网安备 33010602011771号