有点传递闭包的思想感觉这题(无聊倒装
首先为了便于处理,将 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;
}
标签:闭包,字符,mat,int,re,区间,P4290,对应,dp
From: https://www.cnblogs.com/wenzieee/p/18361789