Loading

【笔记】线头 DP/【题解】[BalticOI2013] Vim

线头 DP 可以理解为线性插头 DP。

对于这道题,我们先将问题转化一下,将所有 \(\texttt{"e"}\) 去掉,问题转化为每次向后面跳若干格子,图中不能经过与跳跃结束的格子相同的位置,或者向前移动一格,标记了一些必须经过的格子,求代价最小的路径。

显然是每次后跳若干次,然后向前移动一段,向前移动的段不相交。转化到这一步都不困难。

然后我们可以直接 DP,记录 \(f_{i,j}\) 表示最后一段向前的段是 \(i \to j\),可以简单做到 \(\mathcal{O}(N^3)\),优化一下可以做到 \(\mathcal{O}(N^2|S|)\),能拿到 \(60\) 分。

考虑用线头 DP,我们一次考虑 \(i\sim i + 1\) 之间这一段被经过了几次,只可能被经过了 \(1\) 次或者 \(3\) 次,\(f_{i,x}\) 表示经过 \(1\) 次,前 \(i\) 个位置,最后面的插头为 \(x\) 的最小代价,同理 \(g_{i,x,y}\) 表示经过 \(3\) 次,对应的两个插头分别为 \(x,y\) 的最小代价。时间复杂度 \(\mathcal{O}(N|S|^2)\)

#define N 70005
int n, m, f[N][10], g[N][10][10], u[N], v[N]; char s[N];

int main() {
	read(n), scanf("%s", s + 1);
	rp(i, n)if(s[i] != 'e'){
		u[++m] = s[i] - 'a';
		if(s[i - 1] == 'e')v[m] = 1;
	}
	memset(f, 0x3f, sizeof(f));
	memset(g, 0x3f, sizeof(g));
	rep(i, 0, 9)f[1][i] = 2;
	rep(i, 0, m - 1){
		int op = u[i + 1];
		rep(j, 0, 9){
			if(j != op){
				if(!v[i + 1])cmn(f[i + 1][j], f[i][j]);
				cmn(f[i + 1][j], g[i][op][j]);
			}
			cmn(f[i + 1][j], f[i][op] + 2);
			cmn(f[i + 1][j], g[i][op][op] + 2);
			rep(k, 0, 9){
				
				if(k != op && j != op)cmn(g[i + 1][j][k], g[i][j][k] + 1);
				if(k != op)cmn(g[i + 1][j][k], g[i][op][k] + 3);
				if(j != op)cmn(g[i + 1][j][k], g[i][j][op] + 3);
				cmn(g[i + 1][j][k], g[i][op][op] + 5);
				
				if(j != op)cmn(g[i + 1][j][k], f[i][j] + 3);
				cmn(g[i + 1][j][j], f[i][op] + 5);
			}
		}
	}
	cout << f[m][4] - 2 + (n - m) * 2 << endl;
	return 0;
}
posted @ 2022-07-23 19:03  7KByte  阅读(238)  评论(0编辑  收藏  举报