[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\) 种转移所以几乎没往优化转移上思考,所以要耐心分讨。
  • 对于优化转移,善于抓住变量、不变量,可以根据变量数量进行分讨以减少工作量。
posted @ 2026-01-12 19:59  Zwi  阅读(1)  评论(0)    收藏  举报