[BZOJ2461][BeiJing2011]符环(记忆化搜索)

=== ===

这里放传送门

=== ===

题解

记得当时胡策考这题的时候ATP的语文水平差到了极点,这个题目本来是告诉你长度为2*n的串的第i位和第i+n位的对应关系是什么什么样,ATP硬生生给理解成需要把那个串从n的位置对折然后对应比较那样。结果ATP在折腾一个小时搞了n多写满S和D的纸条以后无果。。最后半个小时突然“嘣”的一下大彻大悟但是已经晚了。。。mdzz= =

统计方案数当然要考虑DP。因为填好了前n个字符以后,根据它给定的信息,后n个就被唯一确定了,所以只要DP前n个即可。然而还要求整个串是一个完整的括号匹配,所以左右括号的匹配情况也会影响状态。于是用f[i][x][y][z]表示当前填写到了第i个字符,[1..i]这段区间里有x个未匹配的左括号,[n+1..n+i]这段区间里有y个未匹配的右括号和z个未匹配的左括号。以当前这一位的字符为S为例,它要求第i个和第i+n个是一样的。如果第i位填的是左括号,那么x和z都要+1,但是不需要变动y。也就是不需要一定让n左边那些左括号去匹配n右边对应出现的右括号,因为可能要留着它来匹配n左边出现的其它右括号,这一点需要注意。最后统计答案的时候判断一下x是否等于y就可以,没必要抵消。如果第i位填的是右括号,那么需要进行一个特判。如果z大于0,那么就可以把x和z都减一然后转移。这里是可以抵消的因为z的作用就是匹配它右边出现的右括号。但是当z等于0的时候那个右括号抵消不掉就要被累加到y里面等待被n左边的左括号匹配。

当前位是D的情况是类似讨论的。用记忆化搜索会非常方便。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n;
long long f[60][60][60][60],ans;
char str[60];
long long search(int i,int x,int y,int z){
    if (i==n+1) return (x==y)&&(z==0);
    if (f[i][x][y][z]!=-1) return f[i][x][y][z];
    long long *k;
    k=&f[i][x][y][z];//用指针指一下省去高维数组寻址的时间
    *k=0;//注意每次要把它清零不能带着-1计算
    if (str[i-1]=='S'){
        *k+=search(i+1,x+1,y,z+1);//填写左括号
        if (x!=0)//填写右括号
          if (z!=0) *k+=search(i+1,x-1,y,z-1);
          else *k+=search(i+1,x-1,y+1,z);
    }else{
        if (z!=0) *k+=search(i+1,x+1,y,z-1);
        else *k+=search(i+1,x+1,y+1,z);
        if (x!=0) *k+=search(i+1,x-1,y,z+1);
    }
    return *k;
}
int main()
{
    scanf("%d",&T);
    for (int time=1;time<=T;time++){
        scanf("%s",str);
        n=strlen(str);
        memset(f,-1,sizeof(f));
        ans=search(1,0,0,0);
        printf("%I64d\n",ans);
    }
    return 0;
}

偏偏在最后出现的补充说明

如果遇到数据范围特别小的题,高维DP是一种可行的思路呀。
ATP

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值