P4290 [HAOI2008] 玩具取名(区间dp,传递闭包?)

link

有点传递闭包的思想感觉这题(无聊倒装

首先为了便于处理,将 W,I,N,G 映射为 1,2,3,4

那么处理数据,想到可以用 传递闭包 的思想?感觉差不多,因为这道题有很多一一对应的关系,并且可以由小的对应关系得到大的对应关系

对于每次输入对应的两个字符 \(ab\),定义 \(g[a, b, i]\in\{0,1\}\) 表示对应关系


题目要求给定一个串 \(s\) 和一些对应关系,最终可以转化为哪几个单个字符,相当于我们知道单个字符的对应双字符,然后看看能不能对应上整个 \(s\),从小区间推到大区间,考虑 区间dp

定义 \(f[l, r, x]\in\{0,1\},~x\in\{1,2,3,4\}\) 表示区间 \([l, r]\) 是否可以被“字符”\(x\) 替换掉,也就是对应上

引入中间值 \(k\),将区间分而治之拆分成 \(f[l,k,?]\)\(f[k + 1, r, ?]\)

两个小区间的第三维度?想到 一个字符总是对应两个字符,所以另外两个字符也要分别枚举,不过要能转移的条件肯定要 另外两个字符能对应 \(x\),即:

\[f[l,r,x] = f[l,r,x]~|~(f[l,k,y] ~\&~ f[k+1,r,z]~\&~ g[y,z,x]) \]

哦对了,注意边界初始化,对于区间 [i, i],它肯定能被 i 上的字符替换

忽略枚举 \(x,y,z\) 的常数,还是 \(O(n^3)\)

#include <bits/stdc++.h>
#define re register int 

using namespace std;
const int N = 210;

int n, s[5], mat[N];
int g[5][5][5], f[N][N][5];
char t[N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	mat['W'] = 1, mat['I'] = 2, mat['N'] = 3, mat['G'] = 4;
	
	for (re i = 1; i <= 4; i ++) cin >> s[i];
	for (re i = 1; i <= 4; i ++)
		for (re j = 1; j <= s[i]; j ++)
		{
			string c; cin >> c;
			g[mat[c[0]]][mat[c[1]]][i] = 1;
		}
	string tt; cin >> tt;
	n = tt.size();
	for (re i = 0; i < n; i ++) t[i + 1] = tt[i];
	
	for (re i = 1; i <= n; i ++) f[i][i][mat[t[i]]] = 1;
	for (re len = 1; len <= n; len ++)
		for (re l = 1; l + len - 1 <= n; l ++)
		{
			int r = l + len - 1;
			for (re k = l; k < r; k ++)
				for (re x = 1; x <= 4; x ++)
					for (re y = 1; y <= 4; y ++)
						for (re z = 1; z <= 4; z ++)
							f[l][r][x] = f[l][r][x] | (f[l][k][y] & f[k + 1][r][z] & g[y][z][x]);
		}
	
	bool flag = false;
	if (f[1][n][1]) { flag = true; cout << 'W'; }
	if (f[1][n][2]) { flag = true; cout << 'I'; }
	if (f[1][n][3]) { flag = true; cout << 'N'; }
	if (f[1][n][4]) { flag = true; cout << 'G'; }
	if (!flag) cout << "The name is wrong!";
	cout << '\n';
	
	return 0;
}
posted @ 2024-08-15 20:50  Zhang_Wenjie  阅读(18)  评论(0)    收藏  举报