CF149D Coloring Brackets
感谢所有AC
思路
在只定义两个状态而无法转移后,选择增加两个状态。定义 $f(i,j,u,v)$ 为序列区间 $i$ 到 $j$ 且 $i$ 染成 $u$ 色,$j$ 染成 $v$ 色的方案数。
在转移时需要进行一下分类。
第一类是(),第二类是(……),第三类是(……)……(……)。
第一类可以直接给符合条件的赋值为 1 。第二类则可以从内部即 $f(i+1,j-1,u,v)$ 转移而来。第三类可以从 $f(i,match,u,v)$ 和$f(match+1,j,u,v)$ 转移而来,需要注意每一类情况转移时的计算方式(第二类计数为累加,第三类计数为相乘)。对于第一类情况 )( 也同样赋值为 1 ,因为题目所给的序列是合法的,那么这类情况也必然是合法的。
而改题用循环求解稍微复杂,因为在求解时会受序列的合法性的影响,不如记忆化搜索来得直观和简单。
代码
#include<iostream>
#include<cstring>
#include<stack>
#define maxn 1000
using namespace std;
char kh[maxn];
stack<int> s;
const int mod = 1e9 + 7;
int match[maxn];
long long dp[maxn][maxn][3][3];
void dfs(int l, int r)
{
if (l + 1 == r)
dp[l][r][0][2] = dp[l][r][0][1] = dp[l][r][1][0] = dp[l][r][2][0] = 1;
else if (r == match[l])
{
dfs(l + 1, r - 1);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (j != 1)
{
dp[l][r][0][1] += dp[l + 1][r - 1][i][j];
dp[l][r][0][1] %= mod;
}
if (j != 2)
{
dp[l][r][0][2] += dp[l + 1][r - 1][i][j];
dp[l][r][0][2] %= mod;
}
if (i != 1)
{
dp[l][r][1][0] += dp[l + 1][r - 1][i][j];
dp[l][r][1][0] %= mod;
}
if (i != 2)
{
dp[l][r][2][0] += dp[l + 1][r - 1][i][j];
dp[l][r][2][0] %= mod;
}
}
}
else
{
dfs(l, match[l]), dfs(match[l] + 1, r);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++)
if (x == y && x && y)
continue;
else
{
dp[l][r][i][j] += (dp[l][match[l]][i][x] * dp[match[l] + 1][r][y][j]) % mod;
dp[l][r][i][j] %= mod;
}
}
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> kh + 1;
int len = strlen(kh + 1);
for (int i = 1; i <= len; i++)
{
if (kh[i] == '(')
s.push(i);
else
{
match[s.top()] = i;
match[i] = s.top();
s.pop();
}
}
dfs(1, len);
long long ans = 0;
for(int i=0;i<3;i++)
for (int j = 0; j < 3; j++)
{
ans += dp[1][len][i][j];
ans %= mod;
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号