csp-s2021 括号序列
-
前言
到刚才才发现这道题是如此的简单,写起来比T3还要好。
全都我代码能力太弱了,T3一开始选错了实现方法,调调错错错错调调调调错错错错调调调调错错错错调调……(以下省略一万字),硬生生占用了近三个小时,最后还被扣了12分,最重要的是它让我没有时间去思考T2……
当然失败原因不能全怪T3,我一开始想错了,以为是个线性DP(毕竟远古括号序列匹配问题是线性解决),就准备在朴素的问题上加各种附加条件,最后成功地加到了7维,当时我一看,好家伙这还写个*,于是就放弃了。
之后偶然听谁说它是个区间DP,顿时……
怎么说呢,茅塞顿开豁然开朗,以及愈发坚定了自己是个傻逼的观念。
-
正文
其实就是一个区间DP。按照题目中的描述去写就行了,只是有一点需要注意的,就比如(A)(B)(C)
会被(A)+(B)(C)
和(A)(B)+(C)
重复计算。特殊处理一下,即考虑对于一对括号并列的情况只由第一个“单独超级括号”和后面的一坨东西转移即可,这样就不会出现重复计算的情况。
写起来很简单,甚至1000个字符都用不上。
#include<cstdio>
#define zczc
#define ll long long
const int N=510;
const int mod=1e9+7;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
inline void add(ll &s1,ll s2){
s1+=s2;s1%=mod;return;
}
int m,n;
bool tl[N],tr[N],ts[N];
char w[N];
ll f[N][N],fs[N][N],g[N][N],fg[N][N];
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);read(n);
scanf("%s",w+1);
for(int i=1;i<=m;i++){
g[i][i-1]=true;
tl[i]=(w[i]=='('||w[i]=='?');
tr[i]=(w[i]==')'||w[i]=='?');
ts[i]=(w[i]=='*'||w[i]=='?');
for(int j=i;j<=m&&j<=i+n-1;j++){
g[i][j]=((g[i][j-1]!=0)&&(w[j]=='*'||w[j]=='?'));
}
g[i][i-1]=false;
}
for(int len=1;len<m;len++){
for(int l=1;l+len<=m;l++){
int r=l+len;
if(len==1){
fs[l][r]=f[l][r]=(tl[l]&tr[r]);
continue;
}
if(tl[l]&&tr[r]){
add(fs[l][r],f[l+1][r-1]);
add(fs[l][r],g[l+1][r-1]);
for(int i=l+1;i<r;i++){
add(fs[l][r],g[l+1][i]*f[i+1][r-1]);
add(fs[l][r],g[i][r-1]*f[l+1][i-1]);
}
}
f[l][r]=fs[l][r];
for(int i=l;i<r;i++){
add(fg[l][r],g[l][i]*f[i+1][r]);
add(f[l][r],fs[l][i]*f[i+1][r]);
add(f[l][r],fs[l][i]*fg[i+1][r]);
}
}
}
printf("%lld",f[1][m]);
return 0;
}
一如既往,万事胜意