题解 【POJ1187】 陨石的秘密
解析
考虑到数据范围,其实我们可以用记搜.
设\(f[a][b][c][d]\)表示还剩\(a\)个'{}',\(b\)个"[]",\(c\)个"()",深度\(\leq d\)个数,(注意是小于等于\(d\),这样好统计一些).
然后,回到题目.
我们可以假设当前的串由两个串组成(其中一个可能是空串),
那么根据乘法原理,当前串的方案数就等于左边的串的方案数乘上右边的串的方案数.
因此,我们可以在\(dfs\)时枚举左边的串的情况(当然右边也可以你喜欢就好).
并考虑在套最外面的是什么.
所以,若当前枚举到的是\(i\)个"()",\(j\)个"[]",\(k\)个"{}",
那么当最外面是"()"时,方案数就应是\((0,0,i-1,d-1)*(a,b,c-i,d)\)(\(a,b,c\)为\(dfs\)时的状态)
而最外面是"[]"时,就是\((0,j-1,i,d-1)*(a,b-j,c-i,d)\),
同理,最外面是"{}"时,就是\((k-1,j,i,d-1)*(a-k,b-j,c-i,d)\),
而当\(a,b,c\)都为\(0\)时,\(f[a][b][c][d]\)就为\(1\)(别忘了\(d\)是表示深度为\(0\)~\(d\)的情况数).
当\(d\)=0时,\(f[a][b][c][d]=0\).
那么,打记搜就行了(最后别忘了减掉\(f[a][b][c][d-1]\))
上代码吧:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return f*sum;
}
const int Mod=11380;
int f[15][15][15][35];
int la,lb,lc,ld;
int dfs(int a/*"{}"的数量*/,int b/*"[]"的数量*/,int c/*"()"的数量*/,int d){
if(!a&&!b&&!c) return f[a][b][c][d]=1;
if(d==0) return f[a][b][c][d]=0;
if(f[a][b][c][d]>=0) return f[a][b][c][d];
f[a][b][c][d]=0;
for(int i=0;i<=c;i++){
if(i) f[a][b][c][d]=(f[a][b][c][d]+dfs(0,0,i-1,d-1)*dfs(a,b,c-i,d))%Mod;//"()"在最外面
for(int j=0;j<=b;j++){
if(j) f[a][b][c][d]=(f[a][b][c][d]+dfs(0,j-1,i,d-1)*dfs(a,b-j,c-i,d))%Mod;//"[]"在最外面
for(int k=0;k<=a;k++){
if(k) f[a][b][c][d]=(f[a][b][c][d]+dfs(k-1,j,i,d-1)*dfs(a-k,b-j,c-i,d))%Mod;//"{}"在最外面
}
}
}
return f[a][b][c][d];
}
int main(){
memset(f,0xff,sizeof(f));
la=read();lb=read();lc=read();ld=read();
dfs(la,lb,lc,ld);
if(ld) dfs(la,lb,lc,ld-1);//因为小于等于d所以要减掉(d-1)的情况(就类似于前缀和)
printf("%d\n", ld?((f[la][lb][lc][ld]-f[la][lb][lc][ld-1])%Mod+Mod)%Mod:f[la][lb][lc][ld]);
return 0;
}